基本语法
声明常量用let,声明变量用var,声明函数用func。 类型修饰采用后置语法,省略时由编译器根据上下文自行推导: 声明常量和变量时用冒号指示类型,自带初值时类型修饰可省略。 声明函数时用箭头指示返回类型,有返回值时返回类型不可省略。
常量 变量 函数(有返回值) 函数(无返回值) Swift let i = 10 let i: Int = 10 var i = 10 var i: Int = 10 func f(n: Int) -> Int {return n + 1} func f(n: Int) -> () {} func f(n: Int) -> Void {} func f(n: Int) {} C# const int i = 10; readonly int i = 10; var i = 10; int i = 10; int f(int n){return n + 1;} int f(int n) => n + 1; void f(int n) {} C++ const auto i = 10; const int i = 10; constexpr auto i = 10; constexpr int i = 10; auto i = 10; int i = 10; int f(int n) {return n + 1;} auto f(int n) -> int {return n + 1;} auto f(int n) {return n + 1;} void f(int n) {} auto f(int n) -> void {} auto f(int n) {}
字符串插值(string interpolation)的语法形式为 \(表达式)。
字符串插值 Swift let x = 10, y = 20 let s = "x=\(x), y=\(y + 1)" C# int x = 10, y = 20; var s = $"x={x}, y={y + 1}";
关于运算符,Swift语言中有一些新的运算符:封闭区间,半开区间以及身份比较运算符。 与C语言不同的是,Swift语言中 = += 等赋值运算符不返回值,也不存在递增 ++ 以及递减 -- 运算符。
封闭区间 半开区间 Swift 2...10 2..<10 C# Enumerable.Range(2, 9) Enumerable.Range(2, 8) Java IntStream.rangeClosed(2, 10) IntStream.range(2, 10)
值比较 引用比较 Swift a == b a === b C# Object.Equals(a, b) a.Equals(b) a == b Object.ReferenceEquals(a, b) a == b Java a.equals(b) a == b
数据结构方面,数组,集合,字典以及元组有特殊语法。 数组用中括号,集合用中括号加类型修饰,字典用中括号加冒号,元组用小括号。
数组 集合 字典 元组 Swift let a = [1,2,3] var a = [Int]() let a: Set = [1,2,3] var a = Set<Int>() let d = [1:"a", 2:"b"] var d = [Int: String]() let t = (1, "a") Python a = [1,2,3] a = [] a = set([1,2,3]) a = set([]) d = {1:"a", 2:"b"} d = {} t = (1, "a")
传统流程控制方面,表示分支的有if, guard和switch, 表示循环的有for-in, while以及repeat-while, 表示跳转的有continue, break, fallthrough, return和throw。
元组(Tuples)
元组是一种个数有限,类型可相同或也可不同的有序的值的组合,是一种匿名的结构体。 元组类型本身没有名字,但是成员可以有名字。 元组的类型由成员的个数,类型以及顺序决定,成员的名字不影响元组的类型。 访问元组的成员可以通过模式匹配,也可以通过指定成员的序号或名字。 不存在只包含一个成员的元组。 不包含任何成员的元组类型记作 (),该类型与Void同义。
// http404Error的类型是 (Int, String)
let http404Error = (404, "Not Found")
// 通过模式匹配来访问元组的成员
let (statusCode, statusMessage) = http404Error
let (justTheStatusCode, _) = http404Error
// 通过指定成员的序号来访问元组的成员
print("The status code is \(http404Error.0)")
// http200Status的类型编译期为 (statusCode: Int, description: String),运行期为(Int, String)。
let http200Status = (statusCode: 200, description: "OK")
// 通过指定成员的名字来访问元组的成员
print("The status message is \(http200Status.description)")
可选值(Optionals)
可选值:一种可能没有值的可空类型,声明语法为 类型?,拆包(取出可选值中包含的值)语法为 可选值变量!。 隐式拆包可选值(implicitly unwrapped optionals):一种假定总是有值的可空类型,声明语法为 类型!,引用可选值变量自动拆包,无需加!。 可选绑定(optional binding):在if语句中强制拆包的语法形式。 可选链(optional chaining):通过可选值调用其属性,方法的语法形式。
// 可选值
var possibleString: String?
let forcedString: String = possibleString!
if possibleString != nil {}
// 可选绑定
if let definiteString = possibleString {}
// 可选绑定 可选链
if let definiteString = possibleString?.lowercaseString {}
// 隐式拆包可选值
var assumedString: String! = "abc"
let implicitString: String = assumedString
if assumedString != nil {}
// 可选绑定
if let definiteString = assumedString {}
流程控制(Control Flow)
while以及repeat-while(由do-while改名而来)循环继承自传统的C语言,for-in循环用于遍历序列。// 遍历区间
for index in 1...5 {}
for _ in 1..<3 {}
// 遍历数组
let names = ["Anna", "Alex", "Brian", "Jack"]
for name in names {}
// 遍历字典
let numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
for (animalName, legCount) in numberOfLegs {}
// 遍历字符串
for ch in "Hello".characters {}
if ... else if ... else结构继承自传统的C语言。 guard ... else结构用于表达提前退出(early exit)这种惯用法(检测输入,如不符合要求提早退出函数)。 Swift语言中的switch语句与传统C语言有很大不同: 缺省情况下不掉入下一个case,可以同时指定多个值,可以匹配区间,可以对元组作模式匹配,也可以为值绑定添加条件过滤。 在switch语句中break可用于提前退出某个case,也可用于占位。fallthrough则表示有意掉入下一个case。 break和continue可以带标号。 // 提前退出
func greet(person: [String: String]) {
guard let name = person["name"] else {return}
}
// 多值匹配
let someCharacter: Character = "e"
switch someCharacter {
case "a", "e", "i", "o", "u":
// ...
default:
// ...
}
// 区间匹配
let approximateCount = 62
switch approximateCount {
case 0:
// ...
case 1..<5:
// ...
case 5..<12:
// ...
case 12..<100:
// ...
case 100..<1000:
// ...
default:
// ...
}
// 元组匹配
let somePoint = (1, 1)
switch somePoint {
case (0, 0):
// ...
case (_, 0):
// ...
case (0, _):
// ...
case (-2...2, -2...2):
// ...
default:
// ...
}
// 值绑定
let anotherPoint = (2, 0)
switch anotherPoint {
case (let x, 0):
// ...
case (0, let y):
// ...
case let (x, y):
// ...
}
// 条件过滤
let yetAnotherPoint = (1, -1)
switch yetAnotherPoint {
case let (x, y) where x == y:
// ...
case let (x, y) where x == -y:
// ...
case let (x, y):
// ...
}
函数(Functions)
函数的参数有实参标签(argument label)和形参名(parameter name)。 实参标签如果存在(不为下划线时)调用方必须指定,形参名在函数内使用。 语法形式为 func 函数名(实参标签1 形参名1: 类型1, 实参标签2 形参名2: 类型2 ...) 缺省形式为 func 函数名(实参标签1兼形参名1: 类型1, 实参标签2兼形参名2: 类型2 ...) 即参数缺省实参标签与形参名相同。
缺省形式 完整形式 最简形式 被调用方 func f(la: Int, lb: Int){ la ... lb ... } func f(la na: Int, lb nb: Int){ na ... nb ... } func f(_ na: Int, _ nb: Int){ na ... nb ... } 调用方 f(la: 1, lb: 2) f(la: 1, lb: 2) f(1, 2)
函数形参缺省为只读的in参数,函数内部参数不可修改,参数传递方式为传值。 inout形参加上inout修饰,函数内部参数可修改,修改对调用方可见,参数传递方式为传引用。
参数传递方式 传值 传值 传引用 传引用 关键字 只读in参数 (可读可写)in参数 out参数 inout参数 Swift 无 / / inout C# / 无 out ref
函数的参数可以有缺省值。 函数是一等公民。可以作为函数的参数,也可以成为函数的返回值。
函数的类型(有返回值) 函数的类型(没有返回值) Swift (Int, Int) -> Bool (Int, Int) -> Void C# Func<int, int, bool> Action<int, int> C++ function<bool(int, int)> function<void(int, int)>
函数可以嵌套。 变长参数。最多只能有一个。函数内部该参数被视为数组。
变长参数 Swift func avg(numbers: Double...) -> Double {} C# double Avg(params double[] numbers) {} Java double avg(double... numbers) {}
闭包(Closures)
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
func backwards(s1: String, s2: String) -> Bool {
return s1 > s2
}
// 把函数当作闭包
var reversed = names.sorted(by: backwards)
// 闭包表达式(closure expressions)
reversed = names.sorted(by: { (s1: String, s2: String) -> Bool in
return s1 > s2
})
// 单行闭包
reversed = names.sorted(by: { (s1: String, s2: String) -> Bool in return s1 > s2 } )
// 根据上下文推导参数和返回值的类型
reversed = names.sorted(by: { s1, s2 in return s1 > s2 } )
// 单一表达式闭包
reversed = names.sorted(by: { s1, s2 in s1 > s2 } )
// 缩写参数名
reversed = names.sorted(by: { $0 > $1 } )
// 运算符函数
reversed = names.sorted(by: >)
// 尾随闭包(trailing closure)
reversed = names.sorted() { $0 > $1 }
reversed = names.sorted { $0 > $1 }
函数和闭包都是引用类型。 嵌套函数和闭包均可捕获外围常量和变量。 非逃逸闭包(Nonescaping Closures):作为参数被传入某个函数,只在该函数内部执行的闭包。 逃逸闭包(Escaping Closures):作为参数被传入某个函数,但在该函数返回之后才执行的闭包。 自动闭包(Autoclosures):作为参数被传入某个函数,在调用端以表达式的形式出现,在被调用端被自动创建的闭包。 普通的非自动非逃逸闭包不需要参数类型标注。 非自动逃逸闭包使用 @escaping 标注参数类型。 自动非逃逸闭包使用 @autoclosure 标注参数类型。 自动逃逸闭包同时使用 @autoclosure @escaping 标注参数类型。 逃逸闭包的函数体内需要显式引用 self,而非逃逸闭包的函数体内可以隐式引用 self。
// 非逃逸闭包
func someFunctionWithNoescapeClosure(closure: () -> Void) {
closure()
}
// 逃逸闭包
var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
completionHandlers.append(completionHandler)
}
//
class SomeClass {
var x = 10
func doSomething() {
someFunctionWithEscapingClosure { self.x = 100 }
someFunctionWithNoescapeClosure { x = 200 }
}
}
let instance = SomeClass()
instance.doSomething()
print(instance.x) // prints "200"
completionHandlers.first?()
print(instance.x) // prints "100"
// 非自动非逃逸闭包
var customersInLine = ["Alex", "Ewa", "Barry", "Daniella"]
func serveCustomer(customer customerProvider: () -> String) {
print("Now serving \(customerProvider())!")
}
serveCustomer(customer: { customersInLine.removeAtIndex(0) } ) // prints "Now serving Alex!"
// 自动非逃逸闭包
func serveCustomer(customer customerProvider: @autoclosure () -> String) {
print("Now serving \(customerProvider())!")
}
serveCustomer(customer: customersInLine.removeAtIndex(0)) // prints "Now serving Ewa!"
// 自动逃逸闭包
var customerProviders: [() -> String] = []
func collectCustomerProviders(_ customerProvider: @autoclosure @escaping () -> String) {
customerProviders.append(customerProvider)
}
collectCustomerProviders(customersInLine.removeAtIndex(0))
collectCustomerProviders(customersInLine.removeAtIndex(0))
for customerProvider in customerProviders {
print("Now serving \(customerProvider())!")
}
// prints "Now serving Barry!"
// prints "Now serving Daniella!"