迅速保护弱自我的嵌套回调

时间:2018-07-26 16:52:23

标签: swift callback

我的问题更像是更好的答案练习。 假设我们有多个嵌套的回调层,我们必须使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是引用当前类(可能是模型,可能是视图控制器)的对象,该类可以在任何情况下释放/弹出发生了三个回拨。

2 个答案:

答案 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