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':

Binarian
  • 12,296
  • 8
  • 53
  • 84
  • 1
    Unrelated: You can use `self.method` instead of `method(self)`. Also you don't need to annotate everything as `internal`, that's the default. – Hamish Oct 06 '16 at 16:16
  • @Hamish 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. – Binarian Oct 06 '16 at 16:29
  • 1
    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&A](http://stackoverflow.com/questions/39867568/initialize-lazy-instance-variable-with-value-that-depends-on-other-instance-vari) – Hamish Oct 06 '16 at 16:42

1 Answers1

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() }
newacct
  • 119,665
  • 29
  • 163
  • 224
  • 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. – Binarian Oct 10 '16 at 08:12
  • @Hamish: Well I don't know how the compiler implements them. But from the language point of view, they seem semantically equivalent. – newacct Oct 12 '16 at 23:36