如何防止在 GCD 中释放对象?

How to prevent deallocated objects in GCD?

此函数应该重新安排工作项的执行:

class MyClass {

    var id: String?
    var workItem: DispatchWorkItem?
    var isDoing = false

    func myFunction() {

        guard let id = self.id else { return }

        isDoing = true
        NotificationCenter.default.post(name: MyNotification, object: nil, userInfo: ["id": id])
        workItem?.cancel()

        workItem = DispatchWorkItem {
            self.isDoing = false
            NotificationCenter.default.post(name: MyNotification, object: nil, userInfo: ["id": id])
        }

        if let workItem = workItem { 
            DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + DispatchTimeInterval.seconds(10), execute: workItem)
        }
    }
}

它在开发中工作正常,但设计似乎可疑,所以我问一些基本问题:

  1. 如果 workItem?.cancel() 在队列试图执行 workItem 之前被调用,workItem 可能是零吗?
  2. 当执行 workItem 时,workItem 中的 id 是否可以为零,或者它是否被范围 let id = self.id 保留?
  3. 如果 MyClass 对象已被释放,那么在执行 workItem 时,workItem 中的 isDoing 是否已经被释放?换句话说,当 MyClass 对象被释放时,调度的 workItem 会发生什么?
  1. 不太清楚你的意思。你不会在任何地方取消 workItem

  2. 不,它不能是 nil,因为您正在使用局部变量 - self.id 的副本。通过使用 guard 可以确保局部变量 id 不为 nil,并且闭包保持对捕获值的强引用(默认情况下),因此它不会被释放。

  3. isDoingMyClass 的一个实例变量,所以它不能在 MyClass 实例被释放之前被释放。问题是,在你的情况下 MyClass 不能被释放,因为你正在寻找一个强大的参考周期。默认情况下,闭包保持对捕获值的强引用,并且您正在捕获 self。并且由于 self 保持对 workItem 的强引用,这反过来又保持对捕获 self 的闭包的强引用,因此引用循环。

通常,当捕获 self 时,您使用捕获列表来处理对 self 的弱引用,并检查它是否尚未被释放

workItem = DispatchWorkItem { [weak self] in
    guard let strongSelf = self else { return }
    // do stuff with strongSelf
}