我的问题更像是更好的答案练习。
假设我们有多个嵌套的回调层,我们必须使self
成为weak
的每一层,并且我知道我们可以为每一层编写guard
(请参见代码段1),但是这甚至有必要吗?如果仅在第一层进行防护,是否足够(请参见代码段2)?
如果从引用计数的角度来看,第一个strongself
是否足够好?
代码段1:
let callBack1 = { [weak self] xx in
guard let strongSelf = self { return }
// strongSelf.func(param)
let callBack2 = { [weak self] yy in {
guard let strongSelf = self { return }
// strongSelf.func(param)
let callBack3 = { [weak self] zz in
guard let strongSelf = self { return }
// strongSelf.func(param)
}
}
}
代码段2:
let callBack1 = { [weak self] xx in
guard let strongSelf = self { return }
// strongSelf.func(param)
let callBack2 = { [weak self] yy in {
// strongSelf.func(param)
let callBack3 = { [weak self] zz in
// strongSelf.func(param)
}
}
}
注意:这是我们代码库中的合法案例,不要以为这不会发生。
编辑:为澄清这个问题,我们假设每个回调都是异步发生的,并且self
是引用当前类(可能是模型,可能是视图控制器)的对象,该类可以在任何情况下释放/弹出发生了三个回拨。
答案 0 :(得分:3)
如果您捕获strongSelf
,将在嵌套闭包中使用它。但是,这意味着您的对象将不会被释放。在某些情况下可能是好的,而在其他情况下则可能是坏的。例如,考虑从视图控制器调用这些嵌套的闭包,人们可以在其中搜索城市并提取其中的可用餐厅列表,为示例起见,我们假设这是通过2个嵌套的异步请求完成的。
将调用第一个回调并捕获self
。现在它将一直保留,直到调用第二个回调为止。但是,如果用户退出搜索控制器该怎么办?您是否要阻止它的释放,因为它正在等待您的第二个回调?
基本上,您只需要确定是否要在回调之间捕获对象即可。如果没有,这是另一种可能有用的替代语法:
self?.someMethod()
答案 1 :(得分:3)
我认为@Andriy回答中的大部分内容都是正确的。但是最正确的答案是我们甚至不需要将[weak self]
放在任何嵌套块中。
当我第一次听到同事的声音时,我不想购买它。但是事实是,第一个块将捕获的 self
定义为weak
,并且此捕获的自我将影响当前范围内的所有捕获的自我,换句话说,在第一个区块的{}
中。因此,如果我们在回调的第一层完成了此操作,则无需再应用[weak self]
。
很难从苹果公司找到确切的文档来证明这一点,但是以下具有核心基础的代码片段保留了数量CFGetRetainCount()
,可以证明是事实。
class TestClass {
var name = ""
var block1: (()->Void)?
var block2: (()->Void)?
func test() {
print(CFGetRetainCount(self))
self.block1 = { [weak self] in
self?.block2 = { // [weak self] in
print(CFGetRetainCount(self))
}
self?.block2?()
print(CFGetRetainCount(self))
}
self.block1?()
print(CFGetRetainCount(self))
}
deinit {
print(CFGetRetainCount(self))
}
}
do {
let tstClass = TestClass()
print(CFGetRetainCount(tstClass))
tstClass.test()
print(CFGetRetainCount(tstClass))
}
如果您有兴趣,可以在操场上尝试一下,随时删除关于block2的[weak self]
的注释,您将看到相同的答案。
保留计数始终为2,并且正确调用了deinit
。