文档章节

Swift入门 - 语法

nosand
 nosand
发布于 2014/06/03 15:08
字数 3865
阅读 3870
收藏 141
2014.6.3日,苹果公布最新编程语言Swift,Swift是一种新的编程语言,用于iOS和OS X应用的开发,没有C的兼容性限制,Swift采用安全的编程模式,增加了现代功能,使编程变得容易,更灵活,更有趣,Swift的重新设计,依靠成熟和备受喜爱的Cocoa, Cocoa Touch 框架,是重新构想软件如何开发的机会。

swift 

以下为语法介绍

原文:A Swift Tour

翻译:http://blog.qunee.com/2014/06/swift语法介绍/ 

Hello world - Swift

传统建议新语言的第一个程序是打印“Hello world”,对于Swift是下面的一行代码

println("Hello, world")
如果你用C或者Objective-C编写过代码,这段句法会感觉熟悉,在Swift中,这是一行完整的代码,不需要引入单独的库比如输入输出或者字符串操作,在全局作用域下编写的代码会作为程序的入口,所以你也不需要main函数,你同样不需要在每段代码后面写分号 这次tour将教你使用Swift编程的足够信息,完成各种编程任务,如果你有不理解也不用担心,本次演示的任何内容在后面的章节中都有详细的介绍

简单赋值

使用 let定义常量,使用var定义变量,常量的值不需要在编译的时候知道,但你必须只设置一次,这意味着你可以使用常量来定义这样的数值:一次决定,多处使用
var myVariable = 42
myVariable = 50
let myConstant = 42
常量和变量必须为你指定的类型,但是你并不需要总是显示的指定类型,创建变量或者常量时,直接提供一个数值让编译器知道他的类型,下面的例子中,编译器指定myVariable为整型,因为他的初始值是一个整型 如果初始值没有足够的信息(比如没有初始值),可以在变量后面指定一个类型,通过冒号分割
let implicitInteger = 70
let implicitDouble = 70.0
let explicitDouble: Double = 70
数值不会隐式的转换成另一种类型,你必须转换成另一种类型,显式的指定类型
let label = "The width is "
let width = 94
let widthLabel = label + String(width)
有更简单的方式让字符串中包含数值:在小括号中编写数值,并在括号之前写上(\),如下例:
let apples = 3
let oranges = 5
let appleSummary = "I have \(apples) apples."
let fruitSummary = "I have \(apples + oranges) pieces of fruit."
使用中括号[]创建数组和字典,通过序号或者key来访问集合中的元素
var shoppingList = ["catfish", "water", "tulips", "blue paint"]
shoppingList[1] = "bottle of water"

var occupations = [
  "Malcolm": "Captain",
  "Kaylee": "Mechanic",
]
occupations["Jayne"] = "Public Relations"
创建一个空的数组或者字典,使用下面的初始化语法
let emptyArray = String[]()
let emptyDictionary = Dictionary<String, Float>()
如果类型信息可以被断定,你可以写一个空数组[],或者一个空的字典[:],比如当你为一个变量设置新的值,或者给函数传递一个参数
shoppingList = []   // Went shopping and bought everything.

控制流

使用if和switch判断条件,使用for-in, for, while和do-while 循环,条件或者循环参数的小括号是可选的,但是主体的花括号是必须的
let individualScores = [75, 43, 103, 87, 12]
var teamScore = 0
for score in individualScores {
  if score > 50 {
    teamScore += 3
  } else {
    teamScore += 1
  }
}
teamScore
if声明中,条件必须是Boolean表达式,这意味着类似 if score{…}de的写法是错误的,不会隐式的与0比较 你可以将if和let结合使用,这意味着数值为可选,可选值可能是一个数值也可能是nil表示值缺失,在类型后面标记问号(?)表示这个值为可选
var optionalString: String? = "Hello"
optionalString == nil

var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName {
  greeting = "Hello, \(name)"
}
如果可选值为nil,条件为flase,代码会被跳过,否则会进入并设置常量,使得变量在代码体中可用 Switch支持任何类型和多种操作符,不限于整型和相等操作
let vegetable = "red pepper"
switch vegetable {
case "celery":
  let vegetableComment = "Add some raisins and make ants on a log."
case "cucumber", "watercress":
  let vegetableComment = "That would make a good tea sandwich."
case let x where x.hasSuffix("pepper"):
  let vegetableComment = "Is it a spicy \(x)?"
default:
  let vegetableComment = "Everything tastes good in soup."
}
在执行完匹配的条件后,程序将跳出选择条件,不会继续到下一个条件,所以不需要在每个条件中指定break
let interestingNumbers = [
  "Prime": [2, 3, 5, 7, 11, 13],
  "Fibonacci": [1, 1, 2, 3, 5, 8],
  "Square": [1, 4, 9, 16, 25],
]
var largest = 0
for (kind, numbers) in interestingNumbers {
  for number in numbers {
    if number > largest {
      largest = number
    }
  }
}
largest
使用while重复一段代码块知道条件改变,循环条件也可以放在后面,这样可以确保循环被至少调用一次
var n = 2
while n < 100 {
  n = n * 2
}
n

var m = 2
do {
  m = m * 2
} while m < 100
m
可以使用序号遍历,既可以使用.. 指定序号范围,也可以写完整的表达式
var firstForLoop = 0
for i in 0..3 {
  firstForLoop += i
}
firstForLoop

var secondForLoop = 0
for var i = 0; i < 3; ++i {
  secondForLoop += 1
}
secondForLoop

函数与闭包

使用 func声明一个函数,通过函数名和参数调用一个函数,使用 -> 声明函数返回类型
func greet(name: String, day: String) -> String {
  return "Hello \(name), today is \(day)."
}
greet("Bob", "Tuesday")
使用值组表示多个数值返回
func getGasPrices() -> (Double, Double, Double) {
  return (3.59, 3.69, 3.79)
}
getGasPrices()
函数还可以设置可变参数,会将他们整合到一个数组中
func sumOf(numbers: Int...) -> Int {
  var sum = 0
  for number in numbers {
    sum += number
  }
  return sum
}
sumOf()
sumOf(42, 597, 12)
函数可以嵌套,嵌套函数可以访问外部函数的声明的变量,对于很长或者复杂的函数,你可以使用嵌套函数来组织代码
func returnFifteen() -> Int {
  var y = 10
  func add() {
    y += 5
  }
  add()
  return y
}
returnFifteen()
函数也是一种类型(第一等类型),这意味着你可以返回一个函数作为函数返回值
func makeIncrementer() -> (Int -> Int) {
  func addOne(number: Int) -> Int {
    return 1 + number
  }
  return addOne
}
var increment = makeIncrementer()
increment(7)
函数还可以作为另一个函数的参数
func hasAnyMatches(list: Int[], condition: Int -> Bool) -> Bool {
  for item in list {
    if condition(item) {
      return true
    }
  }
  return false
}
func lessThanTen(number: Int) -> Bool {
  return number < 10
}
var numbers = [20, 19, 7, 12]
hasAnyMatches(numbers, lessThanTen)
闭包实际上是函数的一种特殊形式,你可以写一个闭包,使用花括号({})包裹代码,没有函数名,使用 in 来分隔传入参数和返回类型
numbers.map({
  (number: Int) -> Int in
  let result = 3 * number
  return result
  })
你可以选择更简洁的闭包方式,当一个闭包类型已知,比如某个委托的回调函数,你可以省略它的参数和返回类型,单声明的闭包默认会返回这个唯一的声明变量
numbers.map({ number in 3 * number })
你可以通过序号替代名称来访问参数,这种方式尤其适合非常短的闭包
sort([1, 5, 3, 12, 2]) { $0 > $1 }

对象和类

使用class创建类,类中的属性声明与常量和变量声明一样,只是他位于这个类的上下文中,同样的函数和方法声明也一样
class Shape {
  var numberOfSides = 0
  func simpleDescription() -> String {
    return "A shape with \(numberOfSides) sides."
  }
}
创建一个类实例,使用类名加括号,使用点操作符访问实例的属性和方法
var shape = Shape()
shape.numberOfSides = 7
var shapeDescription = shape.simpleDescription()
这个版本的Shape类缺少一些重要的东西:用于初始化这个类的构造器,可以使用init来创建
class NamedShape {
  var numberOfSides: Int = 0
  var name: String

  init(name: String) {
    self.name = name
  }

  func simpleDescription() -> String {
    return "A shape with \(numberOfSides) sides."
  }
}
注意,使用self来就区别传入参数的name和自身的name属性,构造器中的构造参数与函数参数的传递方式一样,所有的属性都需要指定值,可以在声明时指定(如numberOfSides)也可以在构造函数中指定(如name) 如果你希望在对象被回收时执行清除动作,可以使用deinit创建一个析构器 子类名包含在父类名称之后,使用冒号分隔,类不需要从任何标准的根类继承,所以你可以包含或者忽略父类 子类中的重写方法需要标记上override,如果没有 override,会被编译器检查出错,同样编译器还会检测带override标志的函数是否真的存在于父类中
class Square: NamedShape {
  var sideLength: Double

  init(sideLength: Double, name: String) {
    self.sideLength = sideLength
    super.init(name: name)
    numberOfSides = 4
  }

  func area() ->  Double {
    return sideLength * sideLength
  }

  override func simpleDescription() -> String {
    return "A square with sides of length \(sideLength)."
  }
}
let test = Square(sideLength: 5.2, name: "my test square")
test.area()
test.simpleDescription()
除了简单属性外,属性还可以具备getter和setter方法
class EquilateralTriangle: NamedShape {
  var sideLength: Double = 0.0

  init(sideLength: Double, name: String) {
    self.sideLength = sideLength
    super.init(name: name)
    numberOfSides = 3
  }

  var perimeter: Double {
  get {
    return 3.0 * sideLength
  }
  set {
    sideLength = newValue / 3.0
  }
  }

  override func simpleDescription() -> String {
    return "An equilateral triagle with sides of length \(sideLength)."
  }
}
var triangle = EquilateralTriangle(sideLength: 3.1, name: "a triangle")
triangle.perimeter
triangle.perimeter = 9.9
triangle.sideLength
在perimeter的setter函数中,新值隐式的命名为newValue,你也可以在set后面的括号中提供一个显式的名称 注意EquilateralTriangle类的构造函数有三步: 1、为子类声明的属性设置值 2、调用父类构造器 3、改变父类定义的属性值,如何用到方法或者getter, setter的工作可以在这之后执行 如果你不需要控制属性,但需要在属性设置前后做些处理,可以使用willSet和didSet,举例,下面的类中可以确保三角形边长与方形边长相同
class TriangleAndSquare {
  var triangle: EquilateralTriangle {
  willSet {
    square.sideLength = newValue.sideLength
  }
  }
  var square: Square {
  willSet {
    triangle.sideLength = newValue.sideLength
  }
  }
  init(size: Double, name: String) {
    square = Square(sideLength: size, name: name)
    triangle = EquilateralTriangle(sideLength: size, name: name)
  }
}
var triangleAndSquare = TriangleAndSquare(size: 10, name: "another test shape")
triangleAndSquare.square.sideLength
triangleAndSquare.triangle.sideLength
triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
triangleAndSquare.triangle.sideLength
类中的方法与函数有一个重要区别,函数参数名只在函数体中使用,但方法的参数名在调用方法时也需要(除了第一个参数),默认不论是调用方法时还是在方法内,方法的参数名都是相同的,你可以指定另一个名称用于方法内
class Counter {
  var count: Int = 0
  func incrementBy(amount: Int, numberOfTimes times: Int) {
    count += amount * times
  }
}
var counter = Counter()
counter.incrementBy(2, numberOfTimes: 7)
当使用可选参数时,可以在方法或者属性操作符前加上(?),如果?前是nil,所有?后面的内容都会忽略,整个表达式的值为nil
let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
let sideLength = optionalSquare?.sideLength

枚举与结构

使用enum创建枚举,与类或者其他可命名的类型一样,枚举可以定义方法
enum Rank: Int {
  case Ace = 1
  case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten
  case Jack, Queen, King
  func simpleDescription() -> String {
    switch self {
    case .Ace:
      return "ace"
    case .Jack:
      return "jack"
    case .Queen:
      return "queen"
    case .King:
      return "king"
    default:
      return String(self.toRaw())
    }
  }
}
let ace = Rank.Ace
let aceRawValue = ace.toRaw()
上面的例子中,枚举中的原始值类型是int,所以你必须指定第一个原始值,余下的会按顺序指定,你也可以使用浮点数或者字符串作为枚举的原始类型 使用toRaw和fromRaw函数转换枚举值和原始值
if let convertedRank = Rank.fromRaw(3) {
  let threeDescription = convertedRank.simpleDescription()
}
枚举中的成员数值必须是实际的值,不能通过其他方式设置,实际上对于没有原始值的情况,你也不能为他设置一个
enum Suit {
  case Spades, Hearts, Diamonds, Clubs
  func simpleDescription() -> String {
    switch self {
    case .Spades:
      return "spades"
    case .Hearts:
      return "hearts"
    case .Diamonds:
      return "diamonds"
    case .Clubs:
      return "clubs"
    }
  }
}
let hearts = Suit.Hearts
let heartsDescription = hearts.simpleDescription()
注意枚举中的Hearts成员引用的两种方式:当指派给常量hearts时,使用了Suit.Hearts全名称,而在switch中,使用了.Hearts,因为self 已经知道是suit,在知道类型时,你可以使用这种缩写 使用struct创建结构,结构支持许多类似类的功能,比如方法和构造函数,两者最大的区别是,结构是值传递,类为引用传递
struct Card {
  var rank: Rank
  var suit: Suit
  func simpleDescription() -> String {
    return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
  }
}
let threeOfSpades = Card(rank: .Three, suit: .Spades)
let threeOfSpadesDescription = threeOfSpades.simpleDescription()
枚举成员实例可以拥有关联值,相同枚举成员可以有不同的关联值,你可以在创建实例时设置关联值,关联值与原始值不同,同一个枚举成员的所有实例有相同的原始值,但可以有不同的关联值 举个例子,考虑从服务中获取日升日落的时间,服务可以响应这些信息或者一些异常信息
enum ServerResponse {
  case Result(String, String)
  case Error(String)
}

let success = ServerResponse.Result("6:00 am", "8:09 pm")
let failure = ServerResponse.Error("Out of cheese.")

switch success {
case let .Result(sunrise, sunset):
  let serverResponse = "Sunrise is at \(sunrise) and sunset is at \(sunset)."
case let .Error(error):
  let serverResponse = "Failure...  \(error)"
}
注意从VerverResponse中获取的日升日落的具体数值可以在switch条件中获取

协议和扩展

使用protocol声明一个协议
protocol ExampleProtocol {
  var simpleDescription: String { get }
  mutating func adjust()
}
类,枚举,结构都可以使用协议
class SimpleClass: ExampleProtocol {
  var simpleDescription: String = "A very simple class."
  var anotherProperty: Int = 69105
  func adjust() {
    simpleDescription += "  Now 100% adjusted."
  }
}
var a = SimpleClass()
a.adjust()
let aDescription = a.simpleDescription

struct SimpleStructure: ExampleProtocol {
  var simpleDescription: String = "A simple structure"
  mutating func adjust() {
    simpleDescription += " (adjusted)"
  }
}
var b = SimpleStructure()
b.adjust()
let bDescription = b.simpleDescription
注意,在SimpleStructure声明中使用mutating关键词用于标示这个方法会修改结构,而SimpleClass中不需要设置mutating标志,是因为类中的方法总是会修改这个类 使用extension为一个已存在的类型增加函数,比如新的方法或者计算属性,你可以使用扩展,保证任意类型具有相同的协议,不管这个类型是从框架还是类库中来的
extension Int: ExampleProtocol {
  var simpleDescription: String {
  return "The number \(self)"
  }
  mutating func adjust() {
    self += 42
  }
}
7.simpleDescription
你可以像其他命名类型一样使用协议名称,比如,创建一个由类型不同但使用相同协议的对象组成的集合,当你使用这些类型时,可以当做一个协议类型来使用,协议以外的方法将不可用
let protocolValue: ExampleProtocol = a
protocolValue.simpleDescription
// protocolValue.anotherProperty  // Uncomment to see the error
虽然protocolValue在运行时是SimpleClass类型,但编译器会以ExampleProtocol对待,这意味你可以避免访问协议以外的方法

泛型

尖括号中的名称用于创建一个泛型的函数和类型
func repeat(item: ItemType, times: Int) -> ItemType[] {
  var result = ItemType[]()
  for i in 0..times {
    result += item
  }
  return result
}
repeat("knock", 4)
也可以用同样的方式在类,枚举,结构中使用泛型
// Reimplement the Swift standard library's optional type
enum OptionalValue {
  case None
  case Some(T)
}
var possibleInteger: OptionalValue = .None
possibleInteger = .Some(100)
在类型名后使用where指定需求列表,比如指定类型必须实现某个协议,或者要求两种类型相同,或者要求具有某个特定的超类
func anyCommonElements <T, U where T: Sequence, U: Sequence, T.GeneratorType.Element: Equatable, T.GeneratorType.Element == U.GeneratorType.Element> (lhs: T, rhs: U) -> Bool {
  for lhsItem in lhs {
    for rhsItem in rhs {
      if lhsItem == rhsItem {
        return true
      }
    }
  }
  return false
}
anyCommonElements([1, 2, 3], [3])
一般情况下,你可以忽略where,在括号后面简单的写一个协议名或者类型,比如,这与相同

© 著作权归作者所有

共有 人打赏支持
nosand
粉丝 79
博文 40
码字总数 33601
作品 0
徐汇
加载中

评论(18)

BryanYang
BryanYang
谢谢分享,前面挺像 js的
viney
viney
语法没有Go简洁漂亮
欣儿
欣儿
Python加了个花括号?
saw1990
saw1990
各种风格混搭
吕秀才
吕秀才
有没有环境搭建
dosmlp
dosmlp
脚本风格
Ch3rry
Ch3rry
果断Python
Dray
Dray

引用来自“lgscofield”的评论

这货跨平台吗,如果只限apple平台,那还玩儿个毛线啊…

人家也只能拿来做 apple 应用吧,有必要跨平台吗?
justphp
justphp
有python的风格!
嗨椒爆炒小小鱼
嗨椒爆炒小小鱼
感觉好别扭啊,没java优雅,没js看起来简单,比shell又复杂些
使用Swift开发React Native组件(一)

使用Swift开发React Native组件(一) RN已经在圈子里流行很长一段时间了,网上相关的文章和介绍也很多,笔者很早之前也接触过一段时间,但也仅仅只是浅尝即止,这次正好有时间,所以准备深度...

黑血沸腾
2017/05/27
0
0
《Swift入门》关于Swift

关于Swift Swift 是一门新的编程语言,用于编写 iOS 和 OS X 应用程序。Swift 结合了 C 和 Objective-C 的优点并且不受C兼容性的限制。Swift 使用安全的编程模式并添加了很多新特性,这将使编...

微wx笑
2016/04/05
0
0
[更新]Apple Swift学习资料汇总

今年的苹果开发者大会(WWDC)上,公布了ios8的几个新特性,其中包括引入了群聊功能,支持第三方输入法等功能。但更让开发者感兴趣的莫过于Swift语言的发布了。 Swift是apple自创的一门转为coc...

geeklxl
2014/06/05
4.7K
8
学习苹果Swift语言的一些在线资源(2014年9月10日更新)

Swift是苹果为OS X和iOS平台引入的一种新的程序设计语言,这种程序设计语言已经激发了开发者社区的大量关注。仅仅几天之内,在Stack Overflow网站上提出的关于Swift的问题已经有500多个,在G...

tyou
2015/08/11
0
0
15个快速学习苹果Swift编程语言的入门教程

要说今年最火的编程语言是什么。那就非Swift莫属了。当然最主要还是市场炒的比较热,加上官方Swift教程《The Swift Programming Language》在北航的大三学生发起在github上进行协同翻译获得了...

欲思
2014/07/24
19.5K
7

没有更多内容

加载失败,请刷新页面

加载更多

《看图轻松理解数据结构与算法系列》导引贴

最近学习数据结构,遇到一个很喜欢的博主,他的文章图文并茂,理解起来很容易。特此开贴记录,方便反复阅读。 博主主页 https://juejin.im/user/57c3970f79bc440063e58518/posts?sort=popul...

科陆李明
28分钟前
0
0
20.27 分发系统介绍~ 20.30 expect脚本传递参数

分发系统介绍分发系统-expect讲解(也就是一个分发的脚本)场景:业务越来越大,网站app,后端,编程语言是php,所以就需要配置lamp或者lnmp,最好还需要吧代码上传到服务器上;但是因...

洗香香
42分钟前
2
0
设计一个百万级的消息推送系统

前言 首先迟到的祝大家中秋快乐。 最近一周多没有更新了。其实我一直想憋一个大招,分享一些大家感兴趣的干货。 鉴于最近我个人的工作内容,于是利用这三天小长假憋了一个出来(其实是玩了两...

crossoverJie
48分钟前
2
0
软件架构:5种你应该知道的模式

Singleton(单例模式)、仓储模式(repository)、工厂模式(factory)、建造者模式(builder)、装饰模式(decorator)……大概每个上课听讲的程序员都不会陌生——软件的设计模式为我们提供...

好雨云帮
今天
3
0
OSChina 周二乱弹 —— 这只是一笔金钱交易

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @小小编辑:推荐歌曲《暮春秋色》- 窦唯 / 译乐队 《暮春秋色》- 窦唯 / 译乐队 手机党少年们想听歌,请使劲儿戳(这里) @我没有抓狂:跨服聊...

小小编辑
今天
948
18

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部