面试题答案
一键面试Swift中闭包可能引发安全漏洞的场景
- 强引用循环:当闭包捕获了一个对象,而这个对象又持有对闭包的引用时,就会形成强引用循环。例如:
class ViewController: UIViewController {
var completionHandler: (() -> Void)?
override func viewDidLoad() {
super.viewDidLoad()
completionHandler = { [unowned self] in
self.doSomething()
}
// 假设这里调用了completionHandler
completionHandler?()
}
func doSomething() {
print("Doing something")
}
}
在上述代码中,如果没有[unowned self]
捕获列表,completionHandler
闭包会捕获self
,而self
又持有completionHandler
,从而导致强引用循环。
避免安全隐患的方式
- 使用
weak
关键字:weak
用于创建弱引用,被弱引用的对象不会增加引用计数。当对象被释放时,所有指向它的弱引用会自动被设置为nil
。- 示例:
class SomeClass {
var closure: (() -> Void)?
init() {
let weakSelf = weak self
closure = {
if let strongSelf = weakSelf {
strongSelf.doSomething()
}
}
}
func doSomething() {
print("Do something in SomeClass")
}
}
- 使用
unowned
关键字:unowned
创建非拥有引用,和weak
不同,unowned
引用不会自动设置为nil
,因此确保被引用的对象在闭包执行期间一直存在非常重要。- 适合场景:当你确定被引用的对象不会比闭包先释放时使用。如上述
ViewController
示例中,在viewDidLoad
方法中self
在闭包执行期间肯定存在,所以可以使用unowned
。
- 正确的闭包定义和使用方式:
- 明确捕获列表:在定义闭包时,明确写出捕获列表,避免隐式捕获导致意外的强引用循环。
- 适时释放闭包引用:如果闭包不再需要使用,及时将其设置为
nil
,打破可能存在的强引用循环。例如,在ViewController
类中,如果completionHandler
不再使用,可以在合适的生命周期方法(如deinit
)中将其设置为nil
。