闭包捕捉(closure capture)浅析

517 查看

 

根据Swift官方文档,闭包(closure)会自动捕捉其所在上下文中的外部变量,即使是定义这些变量的上下文已经消失。寥寥数字,其实已经将闭包捕捉说的足够清晰明了,只是其中隐含的诸如捕捉的具体含义、捕捉的时机、被捕捉变量的特性和捕捉列表的意义等细节,如不详加研究,使用闭包还是会错误百出,难以挥洒自如。

本文中所有代码均在playground中运行,若欲在实际项目中测试,需做部分修改,但基本逻辑和结论不变
—————————— 本文部分结论和例子根据部分读者意见做了修正更新,感谢他们 ———————————–

一、捕捉的含义

闭包捕捉等同于copy

闭包捕捉某个变量就意味着copy一份这个变量,值类型的变量直接复制值,引用类型的变量复制引用。复制后的变量名同被捕捉的变量,复制后的变量仍为变量,常量仍为常量。

看例子

值类型捕捉

结构体Pet的实例方法printNameClosure()返回一个捕捉了self实例本身的闭包,Pet为值类型,因此//1行代码执行完成后,闭包cl复制了一份存储在变量pet中名为旺旺的Pet实例,那么当存储在变量pet的Pet实例改名为强强时,闭包cl所捕捉的Pet实例不变,名字仍为旺旺,因此输出结果为:

我想您应该会有疑问,为什么上述示例代码不写的更简洁些:

事实上,上述示例代码中的闭包cl并未捕捉任何变量,关于闭包捕捉发生的时机下文中会有详细介绍。

引用类型捕捉

这次Pet类型为类,是引用类型,因此//1行代码执行完成后,闭包cl复制了一份变量pet所指向的名为旺旺的Pet实例引用,此时变量pet与闭包cl捕捉的pet指向同一Pet实例,那么当变量pet所指向的Pet实例改名为强强时,闭包cl所捕捉的Pet实例名字也改为强强,因此输出结果为:


二、引用类型变量被捕捉后的特性

引用类型变量被捕捉意味着变量所指向的类的引用被复制,也即引用计数会加一,因此为强持有。

因为引用类型变量捕捉的强持有特性,有时候会产生引用环,导致内存泄漏,解决办法官网文档已有,这里不再赘述。