将引用转换回闭包内的强引用,内存管理,swift

convert the reference back to a strong one inside the closure, memory management, swift

我正在像下面这样在闭包中试验保留周期

 class Sample {
        deinit {
            print("Destroying Sample")
        }

        func additionOf(a: Int, b:Int) {
            print("Addition is \(a + b)")
        }

        func closure() {                
          dispatch_async(dispatch_get_global_queue(0, 0)) { [weak self] in
            self?.additionOf(3, b: 3)   
            usleep(500)                 
            self?.additionOf(3, b: 5)  
          }
        }
    }

稍后,我正在做

var sample : Sample? = Sample()
sample?.closure()

dispatch_async(dispatch_get_global_queue(0, 0)) {
  usleep(100)
  sample = nil
}

输出将是

Addition is 6
Destroying Sample

这是可以理解的,因为 self 在执行 self?.additionOf(3, b:5)

之前是 nil

如果我通过创建另一个引用 [weak self] 的变量在闭包内部进行更改,如下所示

dispatch_async(dispatch_get_global_queue(0, 0)) { [weak self] in
   guard let strongSelf = self else { return }
   strongSelf.additionOf(3, b: 3)
   usleep(500)
   strongSelf.additionOf(3, b: 5)
}

这次的输出是

Addition is 6
Addition is 8
Destroying C

我的问题是为什么 strongSelfsample = nil 之后不为零。是不是因为在sample = nil

之前在闭包里面被捕获了

让我们考虑你的第二个例子:

dispatch_async(dispatch_get_global_queue(0, 0)) { [weak self] in
    guard let strongSelf = self else { return }
    strongSelf.additionOf(3, b: 3)
    usleep(500)
    strongSelf.additionOf(3, b: 5)
}

这是一个常见的模式,它实际上表示 "if self has been deallocated, return immediately, otherwise establish a strong reference, strongSelf, and maintain this strong reference until the closure finishes."

所以,在你的例子中,当这个调度块开始时,self 不是 nil,所以一旦我们分配 strongSelf 来引用它,我们现在有两个强对此 Sample 对象的引用,包括原始 sample 引用和此闭包内的新 strongSelf 引用。

因此,当您的其他线程删除了自己对该 Sample 对象的强引用 sample 时(通过超出范围或将 sample 显式设置为 nil),这个 strongSelf 强引用仍然存在,并且会阻止对象被释放(或者至少直到这个分派块完成)。

在你上面的评论中,你问:

I still not understand why strongSelf does not get changed since self is nil out...

强引用永远不会设置为 nil 只是因为其他一些强引用(即使它是原始强引用)被设置为 nil。将引用设置为 nil 的行为仅适用于 weak 引用,不适用于强引用。

当您的代码在第一个线程上执行 sample = nil 时,所做的只是删除强引用。它不会删除对象或类似的东西。它只是删除了它的强引用。现在分派的块有它自己的强引用,所有发生在 sample = nil 上的是有两个强引用的对象现在只剩下一个强引用(分派块中的 strongSelf 引用) .只有当这个最终的强引用被移除时,对象才会被释放并调用deinit