根据Swift官方文档,闭包(closure)会自动捕捉其所在上下文中的外部变量,即使是定义这些变量的上下文已经消失。寥寥数字,其实已经将闭包捕捉说的足够清晰明了,只是其中隐含的诸如捕捉的具体含义、捕捉的时机、被捕捉变量的特性和捕捉列表的意义等细节,如不详加研究,使用闭包还是会错误百出,难以挥洒自如。
本文中所有代码均在playground中运行,若欲在实际项目中测试,需做部分修改,但基本逻辑和结论不变
—————————— 本文部分结论和例子根据部分读者意见做了修正更新,感谢他们 ———————————–
一、捕捉的含义
闭包捕捉等同于copy
闭包捕捉某个变量就意味着copy一份这个变量,值类型的变量直接复制值,引用类型的变量复制引用。复制后的变量名同被捕捉的变量,复制后的变量仍为变量,常量仍为常量。
看例子
值类型捕捉
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
import UIKit struct Pet { var name: String init(name: String) { self.name = name } func printNameClosure() -> () -> Void { return { print(self.name) } } } var pet: Pet = Pet(name: "旺旺") let cl = pet.printNameClosure() //1 pet.name = "强强" cl() //2 |
结构体Pet的实例方法printNameClosure()
返回一个捕捉了self
实例本身的闭包,Pet为值类型,因此//1行代码执行完成后,闭包cl
复制了一份存储在变量pet
中名为旺旺
的Pet实例,那么当存储在变量pet
的Pet实例改名为强强
时,闭包cl
所捕捉的Pet实例不变,名字仍为旺旺
,因此输出结果为:
1 |
旺旺 |
我想您应该会有疑问,为什么上述示例代码不写的更简洁些:
1 2 3 4 5 6 7 |
...//此行以前代码不变 var pet: Pet = Pet(name: "旺旺") let cl = { print(pet.name) } pet.name = "强强" cl() |
事实上,上述示例代码中的闭包cl
并未捕捉任何变量,关于闭包捕捉发生的时机下文中会有详细介绍。
引用类型捕捉
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
import UIKit class Pet { var name: String init(name: String) { self.name = name } func printNameClosure() -> () -> Void { return { print(self.name) } } } var pet: Pet = Pet(name: "旺旺") let cl = pet.printNameClosure() //1 pet.name = "强强" cl() //2 |
这次Pet类型为类,是引用类型,因此//1行代码执行完成后,闭包cl
复制了一份变量pet
所指向的名为旺旺
的Pet实例引用,此时变量pet
与闭包cl
捕捉的pet
指向同一Pet实例,那么当变量pet
所指向的Pet实例改名为强强
时,闭包cl
所捕捉的Pet实例名字也改为强强
,因此输出结果为:
1 |
强强 |
二、引用类型变量被捕捉后的特性
引用类型变量被捕捉意味着变量所指向的类的引用被复制,也即引用计数会加一,因此为强持有。
因为引用类型变量捕捉的强持有特性,有时候会产生引用环,导致内存泄漏,解决办法官网文档已有,这里不再赘述。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 812c8f5ec452532522795-11">11 12 13 14 15 ۆ节,如不详加研究,使用闭包还是会错误百出,难以挥洒自如。
本文中所有代码均在playground中运行,若欲在实际项目中测试,需做部分修改,但基本逻辑和结论不变—————————— 本文部分结论和例子根据部分读者意见做了修正更新,感谢他们 ———————————–一、捕捉的含义闭包捕捉等同于copy闭包捕捉某个变量就意味着copy一份这个变量,值类型的变量直接复制值,引用类型的变量复制引用。复制后的变量名同被捕捉的变量,复制后的变量仍为变量,常量仍为常量。 看例子 值类型捕捉
结构体Pet的实例方法
我想您应该会有疑问,为什么上述示例代码不写的更简洁些:
事实上,上述示例代码中的闭包 引用类型捕捉
这次Pet类型为类,是引用类型,因此//1行代码执行完成后,闭包
二、引用类型变量被捕捉后的特性引用类型变量被捕捉意味着变量所指向的类的引用被复制,也即引用计数会加一,因此为强持有。 因为引用类型变量捕捉的强持有特性,有时候会产生引用环,导致内存泄漏,解决办法官网文档已有,这里不再赘述。
|