为什么操作队列中不需要[weak self]或[unowned self]?

Hua*_*ham 3 memory-leaks nsoperationqueue automatic-ref-counting retain-cycle swift

在了解了Swift 的捕获列表以及如何使用它来避免保留循环之后,我不禁注意到一些令人费解的事情OperationQueue:它不需要[weak self]或 来[unowned self]防止内存泄漏。

class SomeManager {
    let queue = OperationQueue()
    let cache: NSCache = { () -> NSCache<AnyObject, AnyObject> in
        let cache = NSCache<AnyObject, AnyObject>()
        cache.name = "huaTham.TestOperationQueueRetainCycle.someManager.cache"
        cache.countLimit = 16
        return cache
    }()

    func addTask(a: Int) {
        queue.addOperation { // "[unowned self] in" not needed?
            self.cache.setObject(a as AnyObject, forKey: a as AnyObject)
            print("hello \(a)")
        }
    }
}

class ViewController: UIViewController {

    var someM: SomeManager? = SomeManager()

    override func viewDidLoad() {
        super.viewDidLoad()
        someM?.addTask(a: 1)
        someM?.addTask(a: 2)
    }

    // This connects to a button.
    @IBAction func invalidate() {
        someM = nil  // Perfectly fine here. No leak.
    }
}
Run Code Online (Sandbox Code Playgroud)

我不明白为什么添加操作不会导致保留周期:SomeManager强烈拥有queue,而后者又强烈拥有添加的闭包。每个添加的闭包都强烈引用回SomeManager。理论上,这应该会创建一个保留周期,导致内存泄漏。然而 Instruments 表明一切都很好。

无泄漏

为什么会这样呢?在其他一些多线程、基于块的 API 中,例如DispatchSource,您似乎需要捕获列表。例如,请参阅Apple 的示例代码ShapeEditThumbnailCache.swift

fileprivate var flushSource: DispatchSource
...
flushSource.setEventHandler { [weak self] in   // Here
    guard let strongSelf = self else { return }

    strongSelf.delegate?.thumbnailCache(strongSelf, didLoadThumbnailsForURLs: strongSelf.URLsNeedingReload)
    strongSelf.URLsNeedingReload.removeAll()
}
Run Code Online (Sandbox Code Playgroud)

但在同一个代码文件中,OperationQueue尽管具有相同的语义,但不需要捕获列表:您将引用的闭包移交给self异步执行:

fileprivate let workerQueue: OperationQueue { ... }
...
self.workerQueue.addOperation {
    if let thumbnail = self.loadThumbnailFromDiskForURL(URL) {
        ...
        self.cache.setObject(scaledThumbnail!, forKey: documentIdentifier as AnyObject)
    }
}
Run Code Online (Sandbox Code Playgroud)

我已经阅读了上面的Swift 捕获列表,以及相关的 SO 答案,例如thisthisthis,但我仍然不知道为什么[weak self]或者在 API 中[unowned self]不需要它们。我也不确定万一没有发现泄漏。OperationQueueDispatchOperationQueue

任何澄清将不胜感激。

编辑

除了下面接受的答案之外,我还发现QuinceyMorris 在 Apple 论坛中的评论非常有帮助。

Sve*_*ven 5

您确实有一个保留周期,但这不会自动导致内存泄漏。队列完成操作后将其释放,从而打破循环。

\n\n

这种临时保留周期在某些情况下非常有用,因为您\xe2\x80\x99不必挂起一个对象并仍然让它完成其工作。

\n\n

作为实验,您可以暂停队列。然后你就会看到内存泄漏。

\n