How to remove strong reference cycle from closure from method?
Asked Answered
E

1

3

Here I have some examples for closure strong reference cycles. If I assign a closure to a stored property, I can use a closure capture list to make the captured reference unowned/weak. But if I assign a method to a stored property closure or assign the method to a closure in the outer scope I can not use a capture list.

What can I do to remove the reference cycle in the last two cases?

Example to create and avoid strong reference cycle with capture list with closure only

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

Example to create strong reference cycle with closure from method

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

Example to create strong reference cycle with setting closure from method from extern

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

Output

Test 'Closure with strong reference to self':

Object with property 'Default value of ClosureClass' is being deinitialized

Test 'Set closure with method intern':

Test 'Set closure with method extern':

Escort answered 6/10, 2016 at 14:44 Comment(3)
Unrelated: You can use self.method instead of method(self). Also you don't need to annotate everything as internal, that's the default.Bummer
@Bummer Oh funny, Xcode 8 does not suggest method if writing self. in the property closure case, missing feature or bug, but it gave me the suggestion for method(self) when I wrote meth. Thanks for pointing out. Interesting that method is not enough for a property, but it is enough for a variable in outer scope.Escort
Needing self.method rather than just method is a quirk with lazy properties – they require an explicit use of self., see for example this Q&ABummer
T
3

self.method is just a syntactic sugar for creating a closure (with the default capture mode, which is strong): { () in self.method() }. If you want to use an explicit capture list, don't use the syntactic sugar -- create a closure (which is what it does anyway) explicitly:

{ [unowned self] () in self.method() }
Topper answered 9/10, 2016 at 18:30 Comment(2)
Oh ok now I get it. self.method or method(self) returns a closure with the code in the braces of the method definition. So if you write self.method() you are not avoiding the creation of the closure with self.method, just executing it afterwards and just packing it in another closure to get the unowned/weak semantics.Escort
@Hamish: Well I don't know how the compiler implements them. But from the language point of view, they seem semantically equivalent.Topper

© 2022 - 2024 — McMap. All rights reserved.