首页 > 解决方案 > 直接在闭包中捕获值时的内存语义

问题描述

我最近在objc.io的这篇文章中阅读了有关捕获列表的内容。我认为这是一个很好的提示,我开始使用它。

虽然没有完全澄清,但我假设以这种方式捕获时没有保留周期,因此您获得了捕获的强引用,但无需担心保留周期。

而且我意识到甚至可以捕获方法,而不仅仅是值:

.subscribe(onNext: { [showErrorAlert, populate] result in
    switch result {
    case .success(let book):
        populate(book)
    case .error(_):
        showErrorAlert(L10n.errorExecutingOperation.localized)
    }
})

我正在尝试查找与这种捕获方式相关的一些文档,但我找不到任何文档。这种做法安全吗?这是否等于闭包内的[weak self],的通常舞蹈?strongSelf = self

标签: swift

解决方案


这种做法安全吗?这是否等于闭包内的 [weak self], strongSelf = self 的通常舞蹈?

是和否 - 对象的捕获方法也会保留对象。捕获的方法可能正在访问实例中的任何内容,因此保留它是有意义的。

另一方面,捕获属性不会保留实例。

这是一个简短的片段,您可以粘贴到操场上亲自查看:

import UIKit
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

class A {
    let name: String
    init(name: String) {
        self.name = name
    }

    func a() {
        print("Hello from \(name)")
    }

    func scheduleCaptured() {
        print("Scheduling captured method")
        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) { [a] in
            a()
        }
    }

    func scheduleCapturedVariable() {
        print("Scheduling captured variable")
        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) { [name] in
            print("Hello from \(name)")
        }
    }

    deinit {
        print("\(name) deinit")
    }
}

var a1: A? = A(name: "instance 1")
a1?.scheduleCapturedVariable()

var a2: A? = A(name: "instance 2")
a2?.scheduleCaptured()

a1 = nil
a2 = nil

输出是:

Scheduling captured variable
Scheduling captured method
instance 1 deinit
Hello from instance 1
Hello from instance 2
instance 2 deinit

您可以看到instance 2在触发捕获的块之前不会取消初始化,而instance 1在设置为 nil 后立即取消初始化。


推荐阅读