swift - 将闭包作为参数传递时,ReactiveCocoa 保留循环

标签 swift reactive-cocoa

我在 Swift 中使用 ReactiveCocoa 如下:

registerButton?.rac_signalForControlEvents(UIControlEvents.TouchUpInside).subscribeNextAs(registerButtonTapped)

private func registerButtonTapped(button: UIButton){
    // Method here
}

这会创建一个保留周期。

我知道解决方案如下:

registerButton?.rac_signalForControlEvents(UIControlEvents.TouchUpInside).subscribeNextAs({ [weak self] (button:UIButton) in
    self?.registerButtonTapped(button)
})

但这迫使我使用 subscribeNextAs block ,而不是传递该方法的更好的 oneliner。

知道如何在没有保留周期的情况下使用 oneliner 吗?

最佳答案

好的,所以 the answer Jakub Vano 的链接很棒,但它不是相当通用的。它限制您使用没有参数并返回 Void 的函数。使用 Swift 泛型,我们可以更聪明地使用接受任何参数和使用任何返回类型的函数。

所以第一件事是,对于关系,您必须使用一个对象。不能弱引用结构。所以我们将实例类型限制为 AnyObject,这是所有类都遵守的协议(protocol)。我们的函数声明如下所示:

func applyWeakly<Type: AnyObject, Parameters, ReturnValue>(instance: Type, function: (Type -> Parameters -> ReturnValue)) -> (Parameters -> ReturnValue?)

所以函数接受一个实例,一个函数接受一个实例并返回一个Parameters -> ReturnValue 函数。请记住,Swift 中的闭包和函数是可以互换的。整洁!

请注意,我们必须返回一个可选 ReturnValue,因为该实例可能变为nil。稍后我会解决如何解决这个问题。

好的,现在你需要知道一个非常巧妙的技巧:Swift instance methods are actually just curried class methods ,这非常适合我们的需求 🎉

现在我们可以调用 applyWeaklyclass 函数,当您用实例调用它时,它会返回一个实例函数。 applyWeakly 实现非常简单。

func applyWeakly<Type: AnyObject, Parameters, ReturnValue>(instance: Type, function: (Type -> Parameters -> ReturnValue)) -> (Parameters -> ReturnValue?) {
    return { [weak instance] parameters -> ReturnValue? in
        guard let instance = instance else { return nil }
        return function(instance)(parameters)
    }
}

super 棒。那么你将如何使用它?让我们举一个非常简单的例子。我们有一个包含闭包参数的类,该闭包将引用它自己的实例方法(这只是演示引用循环问题——你的问题涉及两个对象,但我简化为一个)。

class MyClass {
    var closure: (String -> String?)!

    func doThing(string: String) -> String {
        return "hi, \(string)"
    }

    init() {
        closure = doThing // WARNING! This will cause a reference cycle
        closure = applyWeakly(self, function: MyClass.doThing)
    }
}

我们必须为 closure 类型使用一个隐式展开的可选类型,这样我们就可以在我们的 init 函数中引用一个实例方法。没关系,只是这个例子的一个限制。

这很好,而且会起作用,但是我们的 closure 类型是 String -> String? 但我们的 doThing 类型是 String -> String。为了返回一个非可选字符串,我们需要强制解包可选 (😱) 或使用 unowned

无主引用类似于弱引用,只是它们是非归零的。这意味着如果对象被释放并且您使用对它的引用,您的应用程序将会爆炸。不过,就您而言,它是适用的。 Here's more info关于无主 vs 弱。

applyUnowned 函数如下所示:

func applyUnowned<Type: AnyObject, Parameters, ReturnValue>(instance: Type, function: (Type -> Parameters -> ReturnValue)) -> (Parameters -> ReturnValue) {
    return { [unowned instance] parameters -> ReturnValue in
        return function(instance)(parameters)
    }
}

我希望这能澄清一些事情——很乐意回答任何后续问题。这个问题已经在我脑海中萦绕了一段时间,我很高兴终于把我的想法记录下来。

关于swift - 将闭包作为参数传递时,ReactiveCocoa 保留循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31140451/

相关文章:

ios - 在 UIImage 上添加一层颜色

swift - 从 Firebase 检索数据不起作用

cocoa-touch - 我如何使用 ReactiveCocoa 实现 promise 模式?

php - 使用 Alamofire 从 PHP 服务器检索 JSON 时出现问题

swift - 删除 Storyboard 中的选项卡栏项目后无法删除选项卡栏(Swift 4)

ios - 使用 Sprite Kit 监视事件而不重复该事件

ios - 使用 ReactiveCocoa 重试命令

swift - 扩展 ReactiveCocoa

ios - RACObserve 似乎不太好用

objective-c - ReactiveCocoa 的 ClangFormat 样式