【问题标题】:Swift: Assign a class method to a handler and handling weak selfSwift:将类方法分配给处理程序并处理弱自我
【发布时间】:2020-03-05 09:23:25
【问题描述】:

我刚刚在我正在开发的应用程序中寻找内存泄漏,并注意到以下内容会产生内存泄漏:

class SubClass {

    var didCloseHandler: (() -> Void)?

}

class MainClass {

    var subClass = SubClass()

    func setup {
        subClass.didCloseHandler = self.didCloseSubClass
    }

    func didCloseSubClass() {
        //
    }

}

这会产生一个保留循环,并且有充分的理由 - didCloseHandler 强烈捕获 MainClass,而 MainClass 强烈捕获 SubClass。

我的问题:在 Swift 中有没有一种方法可以让我在没有保留循环的情况下将类方法分配给处理程序?

是的,我知道我可以使用subClass.didCloseHandler = { [weak self] self?.didCloseSubClass() } 做到这一点。不过,我想知道是否可以在不引入新闭包的情况下完成。

【问题讨论】:

  • 嗨!你试过weak var subClass...吗?
  • 让它weak var subClass = SubClass()
  • 你为什么不想引入一个新的闭包?
  • @Sweeper 主要原因是为了让代码更简单,更易于维护。我给出的示例非常简单,但是假设我的处理程序有 3 个参数 - 在这种情况下,我必须在闭包中传递 3 个参数,如果我重构处理程序,我必须确保我的参数在任何地方都被适当地重命名,等等。

标签: swift memory-leaks retain-cycle


【解决方案1】:

对 MainClass 中的 subClass 进行弱引用

【讨论】:

  • 弱 var subClass = subClass()
  • 我会等待更多答案,但似乎这是唯一可能的方法,所以如果没有其他聪明的答案,我会接受这个。但老实说,对于大多数用例来说可能最好引入闭包。
【解决方案2】:

如果您在其他地方没有对 SubClass 实例的强引用 - 您可以尝试这样的包装器:

func WeakPointer<T: AnyObject>(_ object: T, _ method: @escaping (T) -> () -> Void) -> (() -> Void) {
    return { [weak object] in
        method(object!)()
    }
}

然后像这样使用它:

func setup() {
    subClass.didCloseHandler = WeakPointer(self, MainClass.didCloseSubClass)
}

如果您在 didCloseSubClass 实现中不需要 MainClass 实例的属性 - 您可以将此方法设为 static,这也将解决您的问题。

如果您在其他地方对 SubClass 实例有强引用并且它不会立即被释放 - weak var subClass 会这样做,正如已经提到的那样。

编辑: 我想出了另一个主意。它可能看起来有点复杂,但也许会有所帮助。

import Foundation

class SubClass {

    @objc dynamic func didCloseHandler() {
        print(#function)
    }

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

class MainClass {

    var subClass = SubClass()

    func setup() {
        if let implementation = class_getMethodImplementation(MainClass.self, #selector(didCloseSubClass)),
            let method = class_getInstanceMethod(SubClass.self, #selector(SubClass.didCloseHandler)) {
            method_setImplementation(method, implementation)
        }
    }

    @objc func didCloseSubClass() {
        print(#function)
    }

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

您更改@objc dynamic 方法的闭包并将其实现设置为setup() 中MainClass 的实现。

【讨论】:

  • 非常聪明的解决方案,但我不一定会向任何人推荐这个:D
  • 在闭包中使用 [weak self] 可能是最好的选择,因为它可以更容易地理解代码中发生的事情,但是解决问题很有趣,所以我添加了另一个解决方案:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-06-23
  • 1970-01-01
  • 1970-01-01
  • 2014-02-08
  • 2019-09-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多