Swift

文章来自菜鸟教程。

Swift

数据类型

Int

  一般来说,你不需要专门指定整数的长度Swift提供了一个特殊的整数类型Int,长度与当前平台的原生字长相同。

  • 在32位平台上,IntInt32长度相同。
  • 在64位平台上,IntInt64长度相同。

  除非你需要特定长度的整数,一般来说使用Int就够了。这可以提高代码一致性和可复用性。即使是在32位平台上,Int可以存储的整数范围也可以达到-2,147,483,648 ~ 2,147,483,647,大多数时候这已经足够大了。

UInt

  Swift也提供了一个特殊的无符号类型UInt,长度与当前平台的原生字长相同。

  • 在32位平台上,UInt和UInt32长度相同。
  • 在64位平台上,UInt和UInt64长度相同。

注意: 尽量不要使用UInt,除非你真的需要存储一个和当前平台原生字长相同的无符号整数。除了这种情况,最好使用Int,即使你要存储的值已知是非负的。统一使用Int可以提高代码的可复用性,避免不同类型数字之间的转换,并且匹配数字的类型推断。

浮点数:Float、Double

  • Float 表示32位浮点数
  • Double 表示64位浮点数

布尔值 Bool

  布尔值指逻辑上的值,它们只能是真或者假。truefalse

字符串 String

"Hello, World!"

字符 Character

"C"

可选类型 Optional

  使用可选类型来处理值可能缺失的情况。可选类型表示有值或没有值。

可以按照下面两种方式声明变量:

var optionalInteger: Int?
var optionalInteger: Optional<Int>

// 注意,在类型和?之间没有空格。

  Optional 是一个含有两种情况的枚举,None 和 Some(T),用来表示可能有或可能没有值。任何类型都可以明确声明为(或者隐式转换)可选类型。当声明一个可选类型的时候,要确保用括号给 ? 操作符一个合适的范围。例如,声明可选整数数组,应该写成 (Int[])? 写成 Int[]? 会报错。

  当你声明一个可选变量或者可选属性的时候没有提供初始值,它的值会默认为 nil。

  如果一个可选类型的实例包含一个值,你可以用后缀操作符 !来访问这个值。

optionalInteger = 42
optionalInteger! // 42

  使用操作符!去获取值为nil的可选变量会有运行时错误。

let myString:String? = nil
if myString != nil {
    print(myString)
} else {
    print("字符串myString为 nil")
}
强制解析

  当你确定可选类型确实包含值之后,你可以在可选的名字后面加一个感叹号(!)来获取值。这个感叹号表示"我知道这个可选有值,请使用它。"这被称为可选值的强制解析(forced unwrapping)。

var myString:String?
myString = "Hello, Swift!"
if myString != nil {
    print(myString)
    print(myString!)//强制解析
} else {
    print("myString 值为 nil")
}

使用!来获取一个不存在的可选值会导致运行时错误。使用!来强制解析值之前,一定要确定可选包含一个非nil的值。

自动解析

  你可以在声明可选变量时使用感叹号(!)替换问号(?)。这样可选变量在使用时就不需要再加一个感叹号(!)来获取值,它会自动解析。

var myString:String!
myString = "Hello, Swift!"
if myString != nil {
    print(myString)
} else {
    print("myString 值为 nil")
}
可选绑定

  使用可选绑定(optional binding)来判断可选类型是否包含值,如果包含就把值赋给一个临时常量或者变量。可选绑定可以用在if和while语句中来对可选类型的值进行判断并把值赋给一个常量或者变量。

var myString:String?
myString = "Hello, Swift!"
if let yourString = myString {
    print("你的字符串值为 - \(yourString)")
} else {
    print("你的字符串没有值")
}

数值范围

设备 关键字 区间值
Int8 1字节 -128 ~ 127
UInt8 1字节 0~ 255
Int32 4字节 -2147483648 ~ 2147483647
UInt32 4字节 0~ 4294967295
Int64 8字节 -9223372036854775808 ~ 9223372036854775807
UInt64 8字节 0 ~ 18446744073709551615
Float 4字节 1.2E-38 ~ 3.4E+38
Double 8字节 2.3E-308 ~ 1.7E+308

类型别名

  使用typealias关键字 比如:定义Int别名为pinguiInt

typealias pinguInt = Int

类型推断 类型安全

  Swift是一个类型安全(type safe)的语言。

  由于Swift是类型安全的,所以它会在编译你的代码时进行类型检查(type checks),并把不匹配的类型标记为错误。这可以让你在开发的时候尽早发现并修复错误。

  比如像下面这样写会报错:

var a = 42
a = "This is hello"
print(a)

cannot assign value of type 'String' to type 'Int', a默认为Int类型

类型标注

  当你声明常量或者变量的时候可以加上类型标注(type annotation),说明常量或者变量中要存储的值的类型。如果要添加类型标注,需要在常量或者变量名后面加上一个冒号和空格,然后加上类型名称。

let constB:Float = 3.14159

运算符

算术运算符
  • 、- 、* 、/ 、%

注意:swift3 中已经取消了++、--。

var A = 10
var B = 20
print("A + B 结果为:\(A + B)")
print("A - B 结果为:\(A - B)")
print("A * B 结果为:\(A * B)")
print("B / A 结果为:\(B / A)")
A += 1   // 类似 A++
print("A += 1 后 A 的值为 \(A)")
B -= 1   // 类似 B--
print("B -= 1 后 B 的值为 \(B)")
比较运算符

== 、!= 、> 、< 、>= 、<=

逻辑运算符

&& 、|| 、~

位运算符

~ 、& 、| 、^,分别为取反,按位与,按位或,按位异或。

赋值运算符

= 、+= 、-= 、/= 、%= 、<<= 、>>= 、&= 、^= 、|=

区间运算符(闭区间运算符1...5/半开区间运算符1..<5)
//闭区间运算符
for index in 1...3 {
    print("\(index) * 3 = \(index * 3)")
}

//半开区间运算符
for index in 1..<3 {
    print("\(index) * 3 = \(index * 3)")
}
其它运算符
  • 一元运算符 如: -a
  • 二元运算符 如: 2 + 3
  • 三元运算符 如: a ? b : c

条件语句

if if...else... switch语句

循环语句

for-in for while repeat...while(类似 while 语句,区别在于判断循环条件之前,先执行一次循环的代码块。)

循环控制语句

continue break fallthrough(如果在一个case执行完后,继续执行下面的case,需要使用fallthrough(贯穿)关键字。)

字符串

创建字符串
// 使用字符串字面量
var strA = "Hello, World!"
print( strA )
// String 实例化
var strB = String("Hello, World!")
print( strB )
空字符串

  你可以使用空的字符串字面量赋值给变量或初始化一个String类的实例来初始值一个空的字符串。 我们可以使用字符串属性isEmpty来判断字符串是否为空。

// 使用字符串字面量创建空字符串
var strA = ""
if strA.isEmpty {
    print( "strA 是空的" )
} else {
    print( "strA 不是空的" )
}
    
// 实例化 String 类来创建空字符串
let strB = String()
if strB.isEmpty {
    print( "strB 是空的" )
} else {
    print( "strB 不是空的" )
}
字符串插值

字符串插值是一种构建新字符串的方式,可以在其中包含常量、变量、字面量和表达式。 您插入的字符串字面量的每一项都在以反斜线为前缀的圆括号中。

//字符串插值 
var varA   = 20
let constA = 100
var varC:Float = 20.0
var stringA2 = "\(varA) 乘于 \(constA) 等于 \(varC * 100)"
print( stringA2 )
字符串连接
  1. 可以通过 "+" 连接。

    let strA = "百度:"
    let strB = "http://www.baidu.com"
    var strC = strA + strB
    print( strC )
    
  2. 使用append方法

    var strA:String = "Hello "
    let strB:Character = "G"
    strA.append(strB)
    print("strA=\(strA)")
    
字符串长度
var str = "www.baidu.com"
print("\(str)的长度为\(str.count)")
字符串比较

你可以使用 == 来比较两个字符串是否相等.

var varA2  = "Hello, Swift!"
var varB2   = "Hello, World!"
if varA2 == varB2 {
    print( "\(varA2) 与 \(varB2) 是相等的" )
} else {
    print( "\(varA2) 与 \(varB2) 是不相等的" )
}
Unicode 字符串

Unicode 是一个国际标准,用于文本的编码,Swift 的 String 类型是基于 Unicode建立的。你可以循环迭代出字符串中 UTF-8 与 UTF-16 的编码。

var unicodeString   = "swift"
print("UTF-8 编码: ")
for code in unicodeString.utf8 {
    print("\(code) ")
}

字符

Swift 的字符是一个单一的字符字符串字面量,数据类型为 Character。

let char1: Character = "A"
let char2: Character = "B"
    
print("char1 的值为 \(char1)")
print("char2 的值为 \(char2)")

如果你想在 Character(字符) 类型的常量中存储更多的字符,则程序执行会报错 比如:

let char: Character = "AB"

空字符变量
Swift 中不能创建空的 Character(字符) 类型变量或常量, 会报错。

let char1: Character = ""
var char2: Character = ""
print("char1 的值为 \(char1)")
print("char2 的值为 \(char2)")

遍历字符串中的字符
Swift的String类型表示特定序列的 Character(字符) 类型值的集合。 每一个字符值代表一个 Unicode 字符。
您可通过for-in循环来遍历字符串中的characters属性来获取每一个字符的值。

for ch in "baidu.com".characters {
    print(ch)
}

Swift 数组

Swift 数组使用有序列表存储同一类型的多个值。相同的值可以多次出现在一个数组的不同位置中。

Swift 数组会强制检测元素的类型,如果类型不同则会报错,Swift 数组应该遵循像Array<Element>这样的形式,其中Element是这个数组中唯一允许存在的数据类型。

如果创建一个数组,并赋值给一个变量,则创建的集合就是可以修改的。这意味着在创建数组后,可以通过添加、删除、修改的方式改变数组里的项目。如果将一个数组赋值给常量,数组就不可更改,并且数组的大小和内容都不可以修改。

创建数组

使用构造语法来创建一个由特定数据类型构成的空数组。

var array = [Int]()

创建了一个类型为 Int ,数量为 3,初始值为 2 的数组。

var array = [Int](repeating: 2, count: 3)

创建了含有三个元素的数组。

var array:[Int] = [10, 20, 30]

访问数组

根据数组的索引来访问数组的元素。

var arrayValue = array[index]

index 索引从 0 开始,即索引 0 对应第一个元素,索引 1 对应第二个元素,以此类推。

var someInts = [Int](repeating: 10, count: 3)
var someVar = someInts[0]

print( "第一个元素的值 \(someVar)" )
print( "第二个元素的值 \(someInts[1])" )
print( "第三个元素的值 \(someInts[2])" )

修改数组

你可以使用 append() 方法或者赋值运算符 += 在数组末尾添加元素,如下所示,我们初始化一个数组,并向其添加元素。

var someInts = [Int]()

someInts.append(20)
someInts.append(30)
someInts += [40]

var someVar = someInts[0]

print( "第一个元素的值 \(someVar)" )     // 20
print( "第二个元素的值 \(someInts[1])" ) // 30
print( "第三个元素的值 \(someInts[2])" ) // 40

也可以通过索引修改数组元素的值。

someInts[2] = 50

print( "第一个元素的值 \(someVar)" )     // 20
print( "第二个元素的值 \(someInts[1])" ) // 30
print( "第三个元素的值 \(someInts[2])" ) // 50

遍历数组

可以使用for-in循环来遍历所有数组中的数据项。

var someStrs = [String]()

someStrs.append("Apple")
someStrs.append("Amazon")
someStrs.append("Tencent")
someStrs += ["Google"]

for item in someStrs {
   print(item)
}

如果同时需要每个数据项的值和索引值,可以使用 String 的 enumerate() 方法来进行数组遍历。

var array = [String]()
        
array.append("Apple")
array.append("Amazon")
array.append("Tencent")
array += ["Google"]
    
for (index, item) in array.enumerated() {
    print("在index=\(index)的值为\(item)")
}

合并数组

可以使用加法操作符(+)来合并两种已存在的相同类型数组。新数组的数据类型会从两个数组的数据类型中推断出来。

var intsA = [Int](repeating: 2, count:2)
var intsB = [Int](repeating: 1, count:3)

var intsC = intsA + intsB

for item in intsC {
    print(item)
}

count 属性

使用 count 属性来计算数组元素个数。

var intsA = [Int](count:2, repeatedValue: 2)
var intsB = [Int](count:3, repeatedValue: 1)

var intsC = intsA + intsB

print("intsA 元素个数为 \(intsA.count)")
print("intsB 元素个数为 \(intsB.count)")
print("intsC 元素个数为 \(intsC.count)")

isEmpty 属性

可以通过只读属性 isEmpty 来判断数组是否为空,返回布尔值。

var intsA = [Int](count:2, repeatedValue: 2)
var intsB = [Int](count:3, repeatedValue: 1)
var intsC = [Int]()

print("intsA.isEmpty = \(intsA.isEmpty)")
print("intsB.isEmpty = \(intsB.isEmpty)")
print("intsC.isEmpty = \(intsC.isEmpty)")

Swift 字典

Swift 字典用来存储无序的相同类型数据的集合,Swift 字典会强制检测元素的类型,如果类型不同则会报错。

Swift 字典每个值(value)都关联唯一的键(key),键作为字典中的这个值数据的标识符。

和数组中的数据项不同,字典中的数据项并没有具体顺序。我们在需要通过标识符(键)访问数据的时候使用字典,这种方法很大程度上和我们在现实世界中使用字典查字义的方法一样。
Swift 字典的key没有类型限制可以是整型或字符串,但必须是唯一的。

如果创建一个字典,并赋值给一个变量,则创建的字典就是可以修改的。这意味着在创建字典后,可以通过添加、删除、修改的方式改变字典里的项目。如果将一个字典赋值给常量,字典就不可修改,并且字典的大小和内容都不可以修改。

创建字典

创建一个特定类型的空字典。

var dic = [keyType : ValueType]()

创建一个字典的实例。

var dic : [Int:String] = [1:"One", 2:"Two"]

访问字典

根据字典的key来访问字典。

var value = dic[key]

修改字典

我可以使用 updateValue(forKey:) 增加或更新字典的内容。如果 key 不存在,则添加值,如果存在则修改 key 对应的值。updateValue(_:forKey:)方法返回Optional值。

var someDict:[Int:String] = [1:"One", 2:"Two", 3:"Three"]
var oldVal = someDict.updateValue("One 新的值", forKey: 1)
var someVar = someDict[1]

print("key = 1 旧的值 \(oldVal)")
print("key = 1 的值为 \(someVar)")
print("key = 2 的值为 \(someDict[2])")
print("key = 3 的值为 \(someDict[3])")

也可以通过指定的key来修改字典的值。

someDict[1] = "sfef"

移除Key-Value

可以使用 removeValueForKey() 方法来移除字典 key-value 对。如果 key 存在该方法返回移除的值,如果不存在返回 nil 。

var someDict:[Int:String] = [1:"One", 2:"Two", 3:"Three"]
var removedValue = someDict.removeValue(forKey: 2)
        
print("key = 1 的值为 \(someDict[1])")
print("removedValue 的值为 \(removedValue)")
print("key = 2 的值为 \(someDict[2])")
print("key = 3 的值为 \(someDict[3])")

遍历字典

可以使用 for-in 循环来遍历某个字典中的键值对。

var someDict:[Int:String] = [1:"One", 2:"Two", 3:"Three"] 
for (key, vale) in someDict {
    print("key = \(key)  value = \(vale)")
}

字典转换为数组

可以提取字典的键值(key-value)对,并转换为独立的数组。

var someDict:[Int:String] = [1:"One", 2:"Two", 3:"Three"]

let dictKeys = [Int](someDict.keys)
let dictValues = [String](someDict.values)

print("输出字典的键(key)")

for (key) in dictKeys {
    print("\(key)")
}

print("输出字典的值(value)")

for (value) in dictValues {
    print("\(value)")
}

count 属性

我们可以使用只读的 count 属性来计算字典有多少个键值对。

var someDict:[Int:String] = [1:"One", 2:"Two", 3:"Three"]
print("someDict 含有 \(someDict.count) 个键值对")

isEmpty 属性

可以通过只读属性 isEmpty 来判断字典是否为空,返回布尔值。

var someDict1:[Int:String] = [1:"One", 2:"Two", 3:"Three"]
var someDict2:[Int:String] = [Int:String]()
    
print("someDict1 = \(someDict1.isEmpty)")
print("someDict2 = \(someDict2.isEmpty)")

Swift 函数

Swift 函数用来完成特定任务的独立的代码块。

Swift使用一个统一的语法来表示简单的C语言风格的函数到复杂的Objective-C语言风格的方法。

  • 函数声明: 告诉编译器函数的名字,返回类型及参数。
  • 函数定义: 提供了函数的实体。

Swift 函数包含了参数类型及返回值类型。

函数定义

Swift 定义函数使用关键字 func。

定义函数的时候,可以指定一个或多个输入参数和一个返回值类型。

每个函数都有一个函数名来描述它的功能。通过函数名以及对应类型的参数值来调用这个函数。函数的参数传递的顺序必须与参数列表相同。

函数的实参传递的顺序必须与形参列表相同,-> 后定义函数的返回值类型。

语法

func funcName(形参) -> returnType {
    statement1
    statement2
    ...
    return parameters
}

我们定义了一个函数名为 runoob 的函数,形参的数据类型为 String,返回值也为 String。

func runoob(site:String) -> String {
    return site
}

函数调用

可以通过函数名以及对应类型的参数值来调用函数,函数的参数传递的顺序必须与参数列表相同。

函数可以接受一个或者多个参数,这些参数被包含在函数的括号之中,以逗号分隔。

func runoob(name: String, site: String) -> String {
    return name + site
}

也可以创建不带参数的函数。

func funcname() -> datatype {
   return datatype
}

元祖作为函数返回值

函数返回值类型可以是字符串,整型,浮点型等。

元组与数组类似,不同的是,元组中的元素可以是任意类型,使用的是圆括号。

你可以用元组(tuple)类型让多个值作为一个复合值从函数中返回。

下面定义了一个名为minMax(_:)的函数,作用是在一个Int数组中找出最小值与最大值。

func minMax(array: [Int]) -> (min: Int, max: Int) {
    var currentMin = array[0]
    var currentMax = array[0]
    for value in array[1..<array.count] {
        if value < currentMin {
            currentMin = value
        } else if value > currentMax {
            currentMax = value
        }
    }
    return (currentMin, currentMax)
}

minMax(_:)函数返回一个包含两个Int值的元组,这些值被标记为min和max,以便查询函数的返回值时可以通过名字访问它们。

如果你不确定返回的元组一定不为nil,那么你可以返回一个可选的元组类型。

你可以通过在元组类型的右括号后放置一个问号来定义一个可选元组,例如(Int, Int)?或(String, Int, Bool)?

注意:
可选元组类型如(Int, Int)?与元组包含可选类型如(Int?, Int?)是不同的.可选的元组类型,整个元组是可选的,而不只是元组中的每个元素值。

前面的minMax(:)函数返回了一个包含两个Int值的元组。但是函数不会对传入的数组执行任何安全检查,如果array参数是一个空数组,如上定义的minMax(:)在试图访问array[0]时会触发一个运行时错误。

为了安全地处理这个"空数组"问题,将minMax(_:)函数改写为使用可选元组返回类型,并且当数组为空时返回nil。

func minMax(array: [Int]) -> (min: Int, max: Int)? {
    if array.isEmpty { return nil }
    var currentMin = array[0]
    var currentMax = array[0]
    for value in array[1..<array.count] {//从第二个元素开始遍历
        if value < currentMin {
            currentMin = value
        } else if value > currentMax {
            currentMax = value
        }
    }
    return (currentMin, currentMax)
}

没有返回值函数

func funcname(site:String) {
    ...
}

函数参数名称

函数参数都有一个外部参数名和一个局部参数名。

局部参数名在函数的实现内部使用。

可以在局部参数名前指定外部参数名,中间以空格分隔,外部参数名用于在函数调用时传递给函数的参数。

func pow(firstArg a: Int, secondArg b: Int) -> Int {
   var res = a
   for _ in 1..<b {
      res = res * a
   }
   print(res)
   return res
}
pow(firstArg:5, secondArg:3)

注意:如果你提供了外部参数名,那么函数在被调用时,必须使用外部参数名。

可变参数

可变参数可以接受零个或多个值。函数调用时,你可以用可变参数来指定函数参数,其数量是不确定的。

可变参数通过在变量类型名后面加入(...)的方式来定义。

func vari<N>(members: N...){
    for i in members {
        print(i)
    }
}
vari(members: 4,3,5)

常量、变量及I/O参数

一般默认在函数中定义的参数都是常量参数,也就是这个参数你只可以查询使用,不能改变它的值。

如果想要声明一个变量参数,可以在参数定义前加 inout 关键字,这样就可以改变这个参数的值了。

func swapTwoInts(_ a: inout Int, _ b: inout Int) {
    let temporaryA = a
    a = b
    b = temporaryA
}


var x = 1
var y = 5
swapTwoInts(&x, &y)
print("x 现在的值 \(x), y 现在的值 \(y)")

注意:注意的是,在传入 swapTwoInts(::) 函数前,都加了 & 的前缀。

函数的类型及使用

每个函数都有种特定的函数类型,由函数的参数类型和返回类型组成。

使用函数类型

在 Swift 中,使用函数类型就像使用其他类型一样。例如,你可以定义一个类型为函数的常量或变量,并将适当的函数赋值给它。

var addition: (Int, Int) -> Int = sum

"定义一个叫做 addition 的变量,参数与返回值类型均是 Int ,并让这个新变量指向 sum 函数"。

sum 和 addition 有同样的类型,所以以上操作是合法的。

现在,你可以用 addition 来调用被赋值的函数了。

func sum(a: Int, b: Int) -> Int {
   return a + b
}

var addition: (Int, Int) -> Int = sum
print("输出结果: \(addition(40, 89))")//129

函数类型作为参数类型、函数类型作为返回类型

可以将函数作为参数传递给另外一个参数。

func sum(a: Int, b: Int) -> Int {
    return a + b
}
var addition: (Int, Int) -> Int = sum
print("输出结果: \(addition(40, 89))")

func another(addition: (Int, Int) -> Int, a: Int, b: Int) {
    print("输出结果: \(addition(a, b))")
}
another(addition: sum, a: 10, b: 20)

函数嵌套

函数嵌套指的是函数内定义一个新的函数,外部的函数可以调用函数内定义的函数。

func calcDecrement(forDecrement total: Int) -> () -> Int {
   var overallDecrement = 0
   func decrementer() -> Int {
      overallDecrement -= total
      return overallDecrement
   }
   return decrementer
}
let decrem = calcDecrement(forDecrement: 30)
print(decrem()) //-30

说明:一个calcDecrement函数,它有一个Int型的参数total,并且他还有一个外不去参数名字forDecrement,在调用的时候必须使用这个外部名字,返回一个()->Int函数。

Swift 闭包

闭包(Closures)是自包含的功能代码块,可以在代码中使用或者用来作为参数传值。

Swift 中的闭包与 C 和 Objective-C 中的代码块(blocks)以及其他一些编程语言中的 匿名函数比较相似。

全局函数和嵌套函数其实就是特殊的闭包。

闭包的形式:

全局函数 嵌套函数 闭包表达式
有名字但不能捕获任何值 有名字,也能捕获封闭函数内的值 无名闭包,使用轻量级语法,可以根据上下文环境捕获值

Swift中的闭包有很多优化的地方:

  1. 根据上下文推断参数和返回值类型
  2. 从单行表达式闭包中隐式返回(也就是闭包体只有一行代码,可以省略return)
  3. 可以使用简化参数名,如0,1(从0开始,表示第i个参数...)
  4. 提供了尾随闭包语法(Trailing closure syntax)

语法:

以下定义了一个接收参数并返回指定类型的闭包语法:

{(parameters) -> return type in
   statements
}

以下闭包形式接收两个参数并返回布尔值:

{(Int, Int) -> Bool in
   Statement1
   Statement 2
    ---
   Statement n
}
let divide = {(val1: Int, val2: Int) -> Int in 
   return val1 / val2 
}
let result = divide(200, 20)
print (result) //10

闭包表达式

闭包表达式是一种利用简洁语法构建内联闭包的方式。 闭包表达式提供了一些语法优化,使得撰写闭包变得简单明了。

sorted 方法

Swift 标准库提供了名为 sorted(by:) 的方法,会根据您提供的用于排序的闭包函数将已知类型数组中的值进行排序。

排序完成后,sorted(by:) 方法会返回一个与原数组大小相同,包含同类型元素且元素已正确排序的新数组。原数组不会被 sorted(by:) 方法修改。

sorted(by:)方法需要传入两个参数:

  1. 已知类型的数组
  2. 闭包函数,该闭包函数需要传入与数组元素类型相同的两个值,并返回一个布尔类型值来表明当排序结束后传入的第一个参数排在第二个参数前面还是后面。如果第一个参数值出现在第二个参数值前面,排序闭包函数需要返回 true,反之返回 false。
let names = ["AT", "AE", "D", "S", "BE"]

// 使用普通函数(或内嵌函数)提供排序功能,闭包函数类型需为(String, String) -> Bool。
func backwards(s1: String, s2: String) -> Bool {
    return s1 > s2
}
var reversed = names.sorted(by: backwards)

print(reversed) //["S", "D", "BE", "AT", "AE"]

参数名称缩写

Swift 自动为内联函数提供了参数名称缩写功能,您可以直接通过0,1,$2来顺序调用闭包的参数。

let names = ["AT", "AE", "D", "S", "BE"]
var reversed = names.sorted( by: { $0 > $1 } )

print(reversed)  //["S", "D", "BE", "AT", "AE"]

说明:0和1表示闭包中第一个和第二个String类型的参数。

如果你在闭包表达式中使用参数名称缩写, 您可以在闭包参数列表中省略对其定义, 并且对应参数名称缩写的类型会通过函数类型进行推断。in 关键字同样也可以被省略。

运算符函数

实际上还有一种更简短的方式来撰写上面例子中的闭包表达式。

Swift 的String类型定义了关于大于号 (>) 的字符串实现,其作为一个函数接受两个String类型的参数并返回Bool类型的值。 而这正好与sort(_:)方法的第二个参数需要的函数类型相符合。 因此,您可以简单地传递一个大于号,Swift可以自动推断出您想使用大于号的字符串函数实现:

let names = ["AT", "AE", "D", "S", "BE"]

var reversed = names.sorted(by: >)
print(reversed)

尾随闭包

尾随闭包是一个书写在函数括号之后的闭包表达式,函数支持将其作为最后一个参数调用。

func someFunctionThatTakesAClosure(closure: () -> Void) {
    // 函数体部分
}

// 以下是不使用尾随闭包进行函数调用
someFunctionThatTakesAClosure({
    // 闭包主体部分
})

// 以下是使用尾随闭包进行函数调用
someFunctionThatTakesAClosure() {
  // 闭包主体部分
}
let names = ["AT", "AE", "D", "S", "BE"]

//尾随闭包
var reversed = names.sorted() { $0 > $1 }
print(reversed) //["S", "D", "BE", "AT", "AE"]

sort() 后的 { 0 >1} 为尾随闭包。

注意: 如果函数只需要闭包表达式一个参数,当您使用尾随闭包时,您甚至可以把()省略掉。reversed = names.sorted { $0 > $1 }

捕获值

闭包可以在其定义的上下文中捕获常量或变量。

即使定义这些常量和变量的原域已经不存在,闭包仍然可以在闭包函数体内引用和修改这些值。

Swift最简单的闭包形式是嵌套函数,也就是定义在其他函数的函数体内的函数。

嵌套函数可以捕获其外部函数所有的参数以及定义的常量和变量。

func makeIncrementor(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0
    func incrementor() -> Int {
        runningTotal += amount
        return runningTotal
    }
    return incrementor
}

一个函数makeIncrementor ,它有一个Int型的参数amout, 并且它有一个外部参数名字forIncremet,意味着你调用的时候,必须使用这个外部名字。返回值是一个()-> Int的函数。

函数题内,声明了变量runningTotal 和一个函数incrementor。

incrementor函数并没有获取任何参数,但是在函数体内访问了runningTotal和amount变量。这是因为其通过捕获在包含它的函数体内已经存在的runningTotal和amount变量而实现。

由于没有修改amount变量,incrementor实际上捕获并存储了该变量的一个副本,而该副本随着incrementor一同被存储。

所以我们调用这个函数时会累加。

func makeIncrementor(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0
    func incrementor() -> Int {
        runningTotal += amount
        return runningTotal
    }
    return incrementor
}

let incrementByTen = makeIncrementor(forIncrement: 10)

// 返回的值为10
print(incrementByTen())

// 返回的值为20
print(incrementByTen())

// 返回的值为30
print(incrementByTen())

闭包是引用类型

上面的例子中,incrementByTen是常量,但是这些常量指向的闭包仍然可以增加其捕获的变量值。

这是因为函数和闭包都是引用类型。

无论您将函数/闭包赋值给一个常量还是变量,您实际上都是将常量/变量的值设置为对应函数/闭包的引用。 上面的例子中,incrementByTen指向闭包的引用是一个常量,而并非闭包内容本身。

这也意味着如果您将闭包赋值给了两个不同的常量/变量,两个值都会指向同一个闭包。

func makeIncrementor(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0
    func incrementor() -> Int {
        runningTotal += amount
        return runningTotal
    }
    return incrementor
}

let incrementByTen = makeIncrementor(forIncrement: 10)

// 返回的值为10
incrementByTen()

// 返回的值为20
incrementByTen()

// 返回的值为30
incrementByTen()

// 返回的值为40
incrementByTen()

let alsoIncrementByTen = incrementByTen

// 返回的值也为50
print(alsoIncrementByTen())

====

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容

  • 这是16年5月份编辑的一份比较杂乱适合自己观看的学习记录文档,今天18年5月份再次想写文章,发现简书还为我保存起的...
    Jenaral阅读 2,729评论 2 9
  • 关于 Swift 重要这个文档所包含的准备信息, 是关于开发的 API 和技术的。这个信息可能会改变, 根据这个文...
    无沣阅读 4,271评论 1 27
  • 132.转换错误成可选值 通过转换错误成一个可选值,你可以使用 try? 来处理错误。当执行try?表达式时,如果...
    无沣阅读 1,232评论 0 3
  • 136.泛型 泛型代码让你可以写出灵活,可重用的函数和类型,它们可以使用任何类型,受你定义的需求的约束。你可以写出...
    无沣阅读 1,450评论 0 4
  • 一滴水看似不起眼但集无数滴水滴却可成汪洋一片;一美金虽然算不了什么但却是亿万富翁所珍视一份。 石油大王约翰·洛...
    王维靖阅读 660评论 0 2