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

时间:2016-11-04 18:24:28

标签: swift function closures strong-references

当一个方法传递给一个带闭包的函数时,我可以在self.someMethod()}中使用someFunc(closure: someMethod) or someFunc(){[unowned self]。

第一个较短但是有很强的参考价值。如何在避免强烈引用的同时使用它?

这是一个包含漏洞和良好漏洞的演示: 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) },强引用就会消失。

有什么想法吗?

2 个答案:

答案 0 :(得分:2)

  

如何在避免强烈引用的同时使用它?

你不能。

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

这只是关于斯威夫特的事实。

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

答案 1 :(得分:0)

马特是对的,如果没有强有力的参考,我找不到使用功能的方法。

我刚刚发现你可以使用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)
    }
}

就像你没有强大的参考,你可以“使用:doSomething”“。

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