为什么操作队列中不需要[弱自我]或[无主自我]?

时间:2018-01-06 03:50:11

标签: swift memory-leaks automatic-ref-counting nsoperationqueue retain-cycle

在了解Swift's capture list以及如何使用它来避免保留周期之后,我不禁注意到有关OperationQueue的疑惑:它不需要[weak self]或{ {1}}以防止内存泄漏。

[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. } } 强烈拥有SomeManager,而queue强烈拥有添加的闭包。每个添加的闭包强烈引用SomeManager。理论上,这应该创建一个导致内存泄漏的保留周期。然而,仪器显示一切都非常好。

No Leak

为什么会这样?在其他一些基于块的多线程API中,如DispatchSource,您似乎需要捕获列表。例如,请参阅ShapeEdit

中的Apple's sample code ThumbnailCache.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()
}

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

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

我已阅读上述Swift's capture list以及相关的SO答案,例如thisthis以及this,但我仍然不知道为什么{{1 [weak self] API中的[unowned self] API中不需要{}} OperationQueue。我也不确定Dispatch案件中是否发现泄漏。

非常感谢任何澄清。

修改

除了下面接受的答案外,我还发现the comment by QuinceyMorris in Apple forums非常有帮助。

1 个答案:

答案 0 :(得分:2)

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

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

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