Swift 中函数的引用以及导致的循环引用场景
Swift 的函数作为一等公民,可以赋值给变量,柯里化,也可以作为参数传递(如果将函数作为参数传递给闭包,只要类型匹配,就可以将函数引用代替内联闭包)。我们可以将函数当作带有名称的特殊闭包,但是使用的时候需要当心。
0x01 问题
最近遇到一个在 Swift 中将函数作为参数传递给闭包时,导致循环引用的场景。
1 | class ClassA { |
实例化ClassB,这个时候就会产生循环引用导致内存泄漏。
0x02 实例函数是柯里化类函数
在Swift中,实例函数只是柯里化类函数,该类函数将实例作为第一个参数,并隐式地使第一个参数作为self
可供函数体使用。 因此,以下两个是等价的:
1 | let numbers = [1, 2, 3, 4, 5, 6, 7, 8] |
而且,这些也是等价的:
1 | let handler1 = self.commandAction |
0x03 可以通过泛型函数来管理内存
如果我们要从上面的 handler2
中获取 self.dynamicType.commandAction
,但是没有参数 (self)
作为参数传递给了包装函数以便引用 self
,我们改怎么办呢? 我们可以通过unowend
来引用,并将 unowned
实例引用传递给类函数获取一个实例函数,而且不会导致循环引用。
1 | func unown<T: AnyObject, V>(_ instance: T, _ classFunction: @escaping (T) -> (() -> V)) -> () -> V { |
这样的话,我们就可以通过以下方式来获取实例方法的引用,而且我们不会强引用self
。
1 | let handler4 = unown(self, self.dynamicType.commandAction) |
缺点是,函数每增加一个参数,我们就需要写一个泛型函数来管理内存。而且,由于使用的是unowned
管理内存,如果使用不当会导致野指针访问导致崩溃。