如何从方法中移除闭包的强引用循环?

3

在这里,我有一些闭包强引用循环的示例。

如果我将一个闭包分配给一个存储属性,我可以使用闭包捕获列表使捕获的引用不可拥有或弱化。但是,如果我将一个方法分配给一个存储属性闭包或将该方法分配给外部范围内的闭包,则无法使用捕获列表。

我该如何消除最后两种情况中的引用循环?

仅使用闭包创建和避免强引用循环的示例

internal class ClosureClass {
    internal let p1: String
    internal lazy var p2: () -> String = {
        [unowned self] // if you comment this out there is a strong reference cycle
        () -> String in
        return self.p1
    }

    internal init() {
        self.p1 = "Default value of ClosureClass"
    }

    deinit {
        print("Object with property '\(self.p1)' is being deinitialized")
    }
}
print("Test 'Closure with strong reference to self':")
var cc: ClosureClass? = ClosureClass.init()
cc!.p2() // lazy need to call it once, else it will not be initiliazed
cc = nil

使用闭包从方法中创建强引用循环的示例

internal class MethodToClosureClass {

    internal let p1: String
    internal lazy var p2: () -> String = method(self) // Why not self.method ? Will create a strong reference cycle, but I can not set the reference to weak or unowned like in closures with the closure capture list

    internal init() {
        self.p1 = "Default value of MethodToClosureClass"
    }

    internal func method() -> String {
        //      [unowned self] in
        return self.p1
    }

    deinit {
        print("Object with property '\(self.p1)' is being deinitialized")
    }
}
print("Test 'Set closure with method intern':")
var m2cc: MethodToClosureClass? = MethodToClosureClass.init()
m2cc!.p2() // lazy need to call it once, else it will not be initiliazed
m2cc = nil

使用从外部方法设置闭包创建强引用循环的示例

internal class MethodClass {
    internal let p1: String
    internal var p2: () -> String = {
        return ""
    }

    internal init() {
        self.p1 = "Default value of MethodClass"
    }

    internal func method() -> String {
        //      [unowned self] in
        return self.p1
    }

    deinit {
        print("Object with property '\(self.p1)' is being deinitialized")
    }
}
print("Test 'Set closure with method extern':")
var mc: MethodClass? = MethodClass.init()
var method: () -> String = mc!.method // will create a strong reference
mc!.p2 = method
mc = nil

输出

测试“具有对self的强引用的闭包”:

拥有属性“ClosureClass的默认值”的对象正在被解除初始化。

测试“使用内部方法设置闭包”:

测试“使用外部方法设置闭包”:


1
不相关的提示:您可以使用 self.method 而不是 method(self)。另外,您不需要将所有内容注释为 “internal”,因为这是默认值。 - Hamish
噢,有趣,Xcode 8在属性闭包情况下写self.时不建议使用method,这是缺少的功能还是bug,但当我写meth时它给了我method(self)的建议。感谢指出。有趣的是,method对于外部作用域中的变量已经足够了,但对于属性来说却不够。 - Binarian
1
需要使用self.method而不是仅使用methodlazy属性的一个怪癖 - 它们需要显式地使用self.,例如请参见此问答 - Hamish
1个回答

3

self.method只是一种语法糖,用于创建一个闭包(默认捕获模式为strong):{ () in self.method() }。 如果要使用明确的捕获列表,请不要使用语法糖 - 显式地创建一个闭包(无论如何它都会这样做):

{ [unowned self] () in self.method() }

哦,现在我明白了。self.method或者method(self)返回一个闭包,其中包含方法定义中大括号内的代码。因此,如果你写self.method(),你并没有避免使用self.method创建闭包,只是在之后执行它,并将其打包到另一个闭包中以获得unowned/weak语义。 - Binarian
@Hamish:嗯,我不知道编译器是如何实现它们的。但从语言的角度来看,它们在语义上似乎是等价的。 - newacct

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接