将闭包作为参数传递时,ReactiveCocoa 保留循环
ReactiveCocoa retain cycle when passing closure as parameter
我在 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
块而不是传递该方法的更好的 oneliner。
知道如何在没有保留周期的情况下使用 oneliner 吗?
好的,所以 Jakub Vano 链接到的 the answer 很棒,但它不是 相当 通用的。它限制您使用没有参数且 returns Void
的函数。使用 Swift 泛型,我们可以更聪明地使用接受 any 参数的函数并使用 any return类型。
所以第一件事是,对于 weak
关系,你 必须 使用一个对象。不能弱引用结构。所以我们将实例类型限制为 AnyObject
,一个所有 classes 都遵守的协议。我们的函数声明如下所示:
func applyWeakly<Type: AnyObject, Parameters, ReturnValue>(instance: Type, function: (Type -> Parameters -> ReturnValue)) -> (Parameters -> ReturnValue?)
所以函数接受一个实例,一个函数接受一个实例,return是一个Parameters -> ReturnValue
函数。请记住,Swift 中的闭包和函数是可以互换的。整洁!
请注意,我们必须 return 一个 可选的 ReturnValue
因为实例可能会变成 nil
。我稍后会解决如何解决这个问题。
好的,现在你需要知道一个非常巧妙的技巧:Swift instance methods are actually just curried class methods,这非常适合我们的需要
所以现在我们可以用 class 函数调用 applyWeakly
,当你用实例调用它时 return 是一个实例函数. 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)
}
}
超级棒。那么你将如何使用它?让我们举一个非常简单的例子。我们有一个 class 和一个包含闭包的参数,该闭包将引用它自己的实例方法(这只是演示引用循环问题——你的问题涉及两个对象,但我简化为一个) .
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
有点恶心。为了 return 一个非可选的字符串,我们需要强制解包一个可选的 () 或使用 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,如下所示:
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
块而不是传递该方法的更好的 oneliner。
知道如何在没有保留周期的情况下使用 oneliner 吗?
好的,所以 Jakub Vano 链接到的 the answer 很棒,但它不是 相当 通用的。它限制您使用没有参数且 returns Void
的函数。使用 Swift 泛型,我们可以更聪明地使用接受 any 参数的函数并使用 any return类型。
所以第一件事是,对于 weak
关系,你 必须 使用一个对象。不能弱引用结构。所以我们将实例类型限制为 AnyObject
,一个所有 classes 都遵守的协议。我们的函数声明如下所示:
func applyWeakly<Type: AnyObject, Parameters, ReturnValue>(instance: Type, function: (Type -> Parameters -> ReturnValue)) -> (Parameters -> ReturnValue?)
所以函数接受一个实例,一个函数接受一个实例,return是一个Parameters -> ReturnValue
函数。请记住,Swift 中的闭包和函数是可以互换的。整洁!
请注意,我们必须 return 一个 可选的 ReturnValue
因为实例可能会变成 nil
。我稍后会解决如何解决这个问题。
好的,现在你需要知道一个非常巧妙的技巧:Swift instance methods are actually just curried class methods,这非常适合我们的需要
所以现在我们可以用 class 函数调用 applyWeakly
,当你用实例调用它时 return 是一个实例函数. 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)
}
}
超级棒。那么你将如何使用它?让我们举一个非常简单的例子。我们有一个 class 和一个包含闭包的参数,该闭包将引用它自己的实例方法(这只是演示引用循环问题——你的问题涉及两个对象,但我简化为一个) .
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
有点恶心。为了 return 一个非可选的字符串,我们需要强制解包一个可选的 () 或使用 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)
}
}
我希望这能澄清事情——很高兴回答任何后续问题。这个问题一直在我脑海里萦绕了一段时间,很高兴终于把我的想法记录下来了。