Swift-闭包
Swift闭包的含义
闭包是自包含的功能代码块,可以用作函数的参数或者返回值
闭包可以捕获上下文中的任何常量以及变量的引用-变量和常量的自封闭
闭包是引用类型
Swift中闭包的表现形式
全局函数:全局函数都是闭包,这些闭包都有命名,但是不能捕获任何值
嵌套函数:嵌套函数也都是闭包,这些闭包有名字,并且能够捕获当前上下文中的常量和变量的值
闭包表达式:闭包表达式都是无名闭包,可以捕获上下文中的值
简单了解一下闭包
-
最简单的闭包:
{}
闭包的执行:
{}()
OK,玩一下最接地气的playground
定义闭包:
闭包执行:
- 项目中经常使用到的闭包
-
懒加载
private lazy var tmpStr:UILabel = { let tmpLabel:UILabel = UILabel() return tmpLabel }()
-
定义网络回调(类似OC网络回调的Block)
typealias NETERRORCLOCURE = (ErrorProtocol) -> Void typealias NETSUCCESSCLOSURE = (AnyObject) -> Void import UIKit class ZHNetWorkApi: NSObject { static func login(phoneNum:String, password:String, netError error:NETERRORCLOCURE? = nil, success successData:NETSUCCESSCLOSURE? = nil){ } }
调用接口:
ZHNetWorkApi.login(phoneNum: "zhu", password: "hou", netError: {[weak self] (error) in guard let instance = self else { return } instance.tmpStr.text = "lin" }) {[weak self] (data) in }
-
官方文档中的闭包
闭包-Closures
Sorted方法-The Sorted Method
闭包表达式-Closure Expression Syntax
类型推断-Inferring Type From Context
-
省略return的单行表达式闭包-Implicit Returns from Single-Expression Closures
- 官方描述:“Single-expression closures can implicitly return the result of their single expression by omitting the return keyword from their declaration” 单一表达式闭包在返回结果的时候可以省略return关键字 - 例子: 我们首先声明一个方法,该方法两个参数,一个NSinteger类型的数据,一个闭包,该闭包有参数有返回值: func closureTest_A(a:NSInteger,closure:((NSInteger, NSInteger) -> NSInteger)) { closure(a, a - 10) } 我们来调用一下这个函数: - 调用方式一: closureTest_A(a: 10) { (aa, bb) -> NSInteger in return aa + bb } 注意:这是一个尾随闭包(下面有介绍),所以闭包可以写在()之外 - 调用方式二: closureTest_A(a: 10) { (aa, bb) -> NSInteger in aa + bb } - 调用方式三: 我可能需要在闭包中做更多的计算操作,然后才返回数据 closureTest_A(a: 10) { (aa, bb) -> NSInteger in let cc = aa - bb return aa + bb } OK,那么问题来了,怎么是不正确的调用呢?很简单,将上面的return省略掉,这时候就会报错“missing return ...”。也就是说,当闭包中含有多个表达式的时候,是不可省略return关键字的(除非闭包没有返回值)。原因是这种情况下,swift无法确定那个表达式才是真正应该返回的数据 那么有的同学就会想了,一个返回Bool类型的闭包中,去执行两个表达式语句,一个是bool类型,一个是NSinteger类型,这种情况下省略return可以自动返回Bool类型的运算结果吗?答案是不会的,还是同样的报错“missing return”,下面是错误的例子 closureTest_A(a: 10) { (aa, bb) -> NSInteger in let cc = aa > bb aa + bb } - 总结: 闭包省略renturn的充要条件: - 闭包有返回值 - 闭包有且只有一条执行语句 - 该条执行语句的结果类型和闭包的返回值类型必须一致
-
参数名简写-Shorthand Argument Names
- 官方描述:“Swift automatically provides shorthand argument names to inline closures, which can be used to refer to the values of the closure’s arguments by the names $0, $1, $2, and so on.” 意思很简单,就是说,访问内联闭包的参数的时候可以用‘$’+下标(第几个参数)的形式。$0就是访问第一个参数,$1就是访问第二个参数。。。 - 官方例子: let reversedNames = names.sorted(isOrderedBefore: { $0 > $1 }) 再看一下这个函数的声明 public func sorted(isOrderedBefore: @noescape (Element, Element) -> Bool) -> [Element] 上面的函数的参数是一个非逃逸闭包,这个非逃逸闭包有两个参数,对这两个参数的访问,可以使用'$' + 下标的方式 而一般我们比较习惯的写法是下面这样的: let bbb = names.sorted {[unowned self] (a, b) -> Bool in return a > b } OK,我们来看看这个方法是怎么一步步演化的 首先是最繁琐的调用-参数名+参数类型(闭包-参数+返回值) let reversedNames = names.sorted(isOrderedBefore: { (a,b) -> Bool in return a > b }) OK我们再简单一点点,怎么简单呢?哈哈,他是一个尾随闭包,再次简化 let reversedNames = names.sorted { (a, b) -> Bool in return a > b } 再来,通过下标访问简化操作 let bbb = names.sorted { return $0 > $1 } 再来,把return也干掉 let bbb = names.sorted { $0 > $1 } - 注意事项,如果闭包的的参数是明确的,也就是 ‘in’和之前的代码没有省略的情况下,是不可以使用'$' + 下标的方式访问闭包参数的,否则会报如下错误“ Anonymous closure arguments cannot be used inside a closure that has explicit arguments” - 让我们来看一个有趣的现象:数组的排序 我们在上面使用过了一个方法 names.sorted,如果你自己试验的时候回发现一个很有其的现象
-
运算符函数-Operator Functions
- 官方描述:
“Classes and structures can provide their own implementations of existing operators. This is known as overloading the existing operators.”
也就是说,类和结构体是能够针对已存在的运算符(‘+’,’-‘,’*‘,’、‘)实现自己的对于这些运算符的操作。这种方式叫做重载运算符。
实际上,运算符不单单可以被重载,如果有需要的话,你也可以定义自己的运算符,比如'+++','---','==='。- 官方例子: reversedNames = names.sorted(isOrderedBefore: >) 上面的例子,是官方文档中给出的例子,可以看到参数是一个'>',按住Commond点击去之后可以查看'>'的定义: public func ><T : Comparable>(lhs: T, rhs: T) -> Bool 很明显这是一个闭包,有两个参数和一个返回值
-
尾随闭包-Trailing Closures
- 官方对尾随闭包的描述
“A trailing closure is written after the function call’s parentheses, even though it is still an argument to the function”
简单来说尾随闭包就是当一个函数或者方法有一个闭包作为参数回调,那么闭包可以写在"()"的后面,而不是写在"()"之间
- 举例子描述-01func funcA(str:NSString, closure:() -> Void) { _ = 1 } 上面定义了一个带有闭包参数的函数,我们看一些对这个函数的调用方式: - 非尾随闭包方式调用 funcA(str: "zhuhoulin", closure: { }) 闭包包含在"()"之内 - 尾随闭包的方式调用 funcA(str: "zhuhoulin") { } 闭包不包含在"()"之内 - 举例子描述-02 func funcB(str:NSString, closure_A:() -> Void, closure_B:() -> Void) { } 上面的例子中有两个闭包参数,同样也会有两种调用方式 - 非尾随闭包 funcB(str: "zhuhoulin", closure_A: { }, closure_B: { }) - 尾随闭包 funcB(str: "zhuhoulin", closure_A: { }) { } 对比上面两种方式,只有最后一个闭包才有成为尾随闭包的这个权利 - 总结-什么是尾随闭包 - 闭包作为函数的参数,并且这个参数是最后一个参数 - 这个闭包包含的代码块不可不用写在"()"之内,而是写在的"()"之后的"{}"之内
值捕获-Capturing Values
闭包是引用类型-Closures Are Reference Types
-
非逃逸闭包-Nonescaping Closures
- 官方对非逃逸闭包的描述: “A closure is said to escape a function when the closure is passed as an argument to the function, but is called after the function returns.” 其实意思很简单,如果一个函数或者方法中,有一个闭包式的参数,如果这个闭包式的参数在return之前已经被执行,那么这个闭包就是非逃逸闭包 - 官方例子: func someFunctionWithNonescapingClosure(closure: @noescape () -> Void) { closure() } 很简单,闭包在函数中执行了就是非逃逸闭包,没有执行就是逃逸闭包 - 个人使用的一个栗子: