首页 > 解决方案 > Swift 为嵌套回调保护弱自我

问题描述

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

标签: swiftcallback

解决方案


我认为@Andriy 的大部分回答都是正确的。但最正确的答案是我们甚至不需要放入[weak self]任何嵌套块。

当我第一次从同事那里听到这个时,我不想买它。但事实是,第一个块定义了被捕获的自我 ,这个被捕获的自我将影响当前范围内所有被捕获的自我,换句话说,在第一个块的. 因此,如果我们在第一层回调中已经完成了,就不需要再申请了。selfweak{}[weak self]

很难从 Apple 那里找到确切的文档来证明这一点,但以下带有核心基础保留计数的代码片段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))
}

如果您有兴趣,可以在您的 Playground 中尝试一下,随时删除对[weak self]for block2 的评论,您将看到相同的答案。

保留计数始终为 2 并且deinit被正确调用。


推荐阅读