swift - 奇怪的弱 self 和保留循环行为

标签 swift retain-cycle

让我们考虑以下代码:

// Just for easier testing
protocol Printer {
    var delayer: Delayer { get }
}

// Retain cycle
class Printer1: Printer {
    private func action() {
        print("action")
    }
    
    private(set) lazy var delayer: Delayer = {
        return Delayer(action)
    }()
    
    deinit {
        print("deinit")
    }
}

// Works fine, but weak mess
class Printer2: Printer {
    private func action() {
        print("action")
    }
    
    private(set) lazy var delayer: Delayer = {
        return Delayer { [weak self] in self?.action() }
    }()
    
    deinit {
        print("deinit")
    }
}

// Questionable hack, but works fine
class Printer3: Printer {
    private func action() {
        print("action")
    }
    
    private(set) lazy var delayer: Delayer = {
        return Delayer(weakAction)
    }()

    // computed property or function is also fine here
    private lazy var weakAction: () -> Void = {
        return { [weak self] in
            self?.action()
        }
    }()
    
    deinit {
        print("deinit")
    }
}

// Retain cycle
class Printer4: Printer {
    private func action() {
        print("action")
    }
    
    private(set) lazy var delayer: Delayer = {
        weak var welf: Printer4? = self
        return Delayer(welf?.action ?? {})
    }()
    
    deinit {
        print("deinit")
    }
}

// Works fine
class Printer5: Printer {
    private func action() {
        print("action")
    }
    
    private(set) lazy var delayer: Delayer = {
        weak var welf: Printer5? = self
        return Delayer { welf?.action() }
    }()
    
    deinit {
        print("deinit")
    }
}

class Delayer {
    private var action: () -> Void
    
    init(_ action: @escaping () -> Void) {
        self.action = action
    }
    
    func run() {
        DispatchQueue.main.asyncAfter(deadline: .now() + 3) { [weak self] in
            self?.action()
        }
    }
}

因此,我们有一个 Printer 类,其中包含一个 Delayer 类,该类在 Printer 上执行操作并延迟执行。

我们这样调用它:

var printer: Printer? = PrinterX()

printer?.delayer.run()

DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
     printer = nil
}

很清楚为什么 Printer1 创建保留周期。 Action 被传递到带有隐式强 self 的 Delayer 中,但由于 Delayer 归 Printer 所有,因此无法释放。

Printer2 在我看来是预期的方式。显然不会创建保留周期,但一直写起来有点困惑。这就是为什么我开始尝试其他解决方案。

我不明白为什么 Printer3 不创建保留周期。因为 weakAction 是 self 的属性,因此将其传递给 Delayer 应该会像 Printer1 中那样创建强引用。

我也不明白为什么 Priner4 会创建保留周期。 welf 是对 self 的本地弱引用,因此在将其传递到 Delayer 时不应增加引用计数。

奇怪的是,在 Printer5 中使用内部闭包的 welf 并不会创建保留周期。

问题

  1. 谁能向我解释一下 Printer3、Printer4 和 Printer5 上的这种奇怪行为
  2. 我很想使用 Printer3 解决方案。使用安全吗?由于它看起来几乎像一个错误,我可以使用它而不用担心它会在未来版本中修复并因此在我的应用程序中创建保留周期吗?

最佳答案

首先,所有打印机都在创建并保留自己的 Delayer。延迟器接受一个闭包,然后保留该闭包。

让我们尝试一一浏览它们。

打印机1

正如您自己所说,它创建保留周期的原因非常清楚。您将 self.action 实例方法作为 Delayer 的闭包传递,并且由于所有闭包都是引用类型,因此传递 self.action 将保留其周围范围(即打印机1)。

打印机2

同样,这里非常明显。您在传递给 Delayer 的闭包内显式捕获对 self 的弱引用,因此不会创建保留周期。

打印机3

这里,没有创建保留循环,因为立即调用 self.weakAction 属性,并且其结果(保存对 self 的弱引用的闭包)被传递给 Delayer。实际上,这与 Printer2 中发生的情况完全相同。

打印机4

首先,您捕获对 self 的弱引用,然后获取 welf?.action 并将结果传递给 Delayer。同样,立即调用 welf?.action,并将结果(指向实例方法的指针)传递给 Delayer。对 self 的弱引用仅在周围作用域(惰性 var 创建作用域)期间保留,并且传递 action 实例方法将保留 self。这与 Printer1 相同。

打印机5

在这里,您首先创建一个对 self 的弱引用,然后在传递给 Delayer 的新闭包中捕获该弱引用。由于 self 在传递的闭包中永远不会被直接引用,因此它不会捕获该范围内的 self,仅捕获 welf 弱引用。这与 Printer2 几乎相同,但语法略有不同。

就我个人而言,我会选择 Printer2 方式(创建一个新的闭包,保留对 self 的弱引用并使用它来调用 self?.action)。它使得代码最容易遵循(而不是保留带有弱捕获 self 的闭包的变量)。但是,根据您的实际用例,这当然可能是有意义的。

关于swift - 奇怪的弱 self 和保留循环行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71585336/

相关文章:

ios - 保留有关ivar的__block变量的循环警告

ios - 在一个 block 中保留循环

swift - 知道保留循环在哪里并删除它们

ios - 快速点击按钮时添加行或文本字段

ios - 本地化 Storyboard

ios - 如何使用对象列中的值作为 wherekey?

ios - 类保留计数

Swift:平面图仍然返回可选

ios - 在 Swift 上编辑 UItextfield 时关闭键盘

ios - 为什么在函数外部引用不创建保留循环?