将方法传递给函数时避免强引用

Avoiding strong reference when passing a method to a function

将方法传递给采用闭包的函数时,我可以使用 someFunc(closure: someMethod) orsomeFunc() { [unowned self] in self.someMethod() }`.

第一个较短,但具有很强的参考意义。如何在避免这种强引用的情况下使用它?

这是一个既有泄漏的也有良好的演示: https://swiftlang.ng.bluemix.net/#/repl/581ccd3a0bdc661a6c566347

import Foundation

private var instanceCounter = 0

class Leak : NSObject {

    override init() {
        super.init()
        instanceCounter += 1
    }

    deinit {
        instanceCounter -= 1
    }
}

class OnFunctionLeak : Leak {

    override init() {
        super.init()
        _ = NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: "OnFunctionLeak"),
                                               object: nil,
                                               queue: nil,
                                               usingBlock: doNothing)
    }

    func doNothing(_ notif: Notification) { }

    deinit {
        NotificationCenter.default.removeObserver(self)
    }
}

class OnClosureLeak : Leak {

    override init() {
        super.init()
        _ = NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: "OnFunctionLeak"),
                                               object: nil,
                                               queue: nil) { [unowned self] notif in
            self.doNothing(notif)
        }
    }

    func doNothing(_ notif: Notification) { }

    deinit {
        NotificationCenter.default.removeObserver(self)
    }
}

var onFunctionLeak: OnFunctionLeak? = OnFunctionLeak()
onFunctionLeak = nil

//XCTAssertEqual(instanceCounter, 0)
print("instanceCounter: \(instanceCounter) == 0")

instanceCounter = 0
var onClosureLeak: OnClosureLeak? = OnClosureLeak()
onClosureLeak = nil

//XCTAssertEqual(instanceCounter, 0)
print("instanceCounter: \(instanceCounter) == 0")

较短的选择在第 26 行,如果我将 doNothing 替换为 { [unowned self] notif in self.doNothing(notif) },强引用将消失。

有什么想法吗?

How can I use it while avoiding this strong reference?

你不能。

只有内联定义的匿名函数(在使用时)可以有一个捕获列表(例如[unowned self])。因此,只有匿名函数才能提供您所要求的功能。用 func 定义的函数根本做不到。

这只是关于 Swift 的事实。

(可能有潜在的原因;我怀疑原因与存储有关。func 函数以某种方式静态存储。但是内联定义的匿名函数不是;它来自在它被传递给被调用者的那一刻成为存在。但这只是一个猜测,而且是一个相当模糊的猜测。)

Matt 是对的,如果没有强引用,我找不到使用函数的方法。

我刚刚发现你可以使用 var 来使它更干净直接在你的函数中编写闭包并不真正干净。

class OnVarLeak : Leak {

    var value = 0

    override init() {
        super.init()
        NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: "OnVarLeak"),
                                               object: nil,
                                               queue: nil,
                                               using: doNothing)
    }

    var doNothing: (Notification) -> Void {
        return { [unowned self] notif in
            self.value += 1
        }
    }

    deinit {
        NotificationCenter.default.removeObserver(self)
    }
}

像这样你没有强引用,你可以"using: doSomething)"。

我仍然认为 Swift 编译让你使用函数而不是闭包是不安全的,因为它总是会在你的项目中留下内存泄漏。