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

yjghlzjz  于 2023-01-02  发布在  Swift
关注(0)|答案(1)|浏览(328)

我正在测试闭包中的保留周期,如下所示

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)之前为空
如果我在闭包内部做了一个修改,创建了另一个引用[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之前被捕获到闭包中吗

enyaitl3

enyaitl31#

让我们考虑一下与你的第二个例子相对应的当代例子:

DispatchQueue.global().async { [weak self] in
    guard let self else { return }

    self.additionOf(3, b: 3)
    Thread.sleep(forTimeInterval: 0.0005)
    self.additionOf(3, b: 5)
}

这是一种常见的模式,它实际上表示“如果self已被释放,则立即释放return,否则建立对self的强引用,并维护此强引用直到闭包完成”。
所以,在你的例子中,如果self不是nil当这个被分派的块开始时,那么一旦我们点击guard语句,我们现在就有了两个对这个Sample对象的强引用,原始的sample引用和这个闭包中的这个新引用。
因此,当其他线程移除它自己对Sample对象的强引用sample时(通过超出作用域或显式将sample设置为nil),该闭包的强引用仍然存在,并将阻止该对象被释放(或至少直到该调度块完成)。
在你上面的评论中,你问:
我仍然不明白为什么strongSelf没有得到改变,因为自我是nil了...
强引用永远不会仅仅因为其他强引用(即使它是原始的强引用)被设置为nil而被设置为nil。引用被设置为nil的行为只适用于weak引用,而不适用于强引用。
当你的代码在第一个线程上执行sample = nil时,它所做的只是移除一个强引用,而不是删除对象或类似的东西,它只是移除它的强引用,现在被调度的块有了自己的强引用,在sample = nil上发生的所有事情是,具有两个强引用的对象现在只剩下一个强引用(在调度块中对它的引用)。只有当这个最后的强引用被移除时,对象才会被释放,并且deinit将被调用。
FWIW,如果您要立即调度到全局队列,[weak self]捕获列表实际上并没有太大的实用性。您可能会删除它,然后执行以下操作:

DispatchQueue.global().async { [self] in
    additionOf(3, b: 3)
    Thread.sleep(forTimeInterval: 0.0005)
    additionOf(3, b: 5)
}

[weak self]捕获列表在闭包运行于某个稍后的时间点时最有用,例如,完成处理程序或asyncAfter,其中self有可能在闭包运行之前超出范围:

DispatchQueue.global().asyncAfter(deadline: .now() + 3) { [weak self] in
    guard let self else { return }

    self.additionOf(3, b: 3)
    Thread.sleep(forTimeInterval: 0.0005)
    self.additionOf(3, b: 5)
}

相关问题