6

I'm having trouble tracking down a retain cycle. I think it's to do with the way I subscribe to events. Pseudo code is like this:

override func viewDidLoad() {
   func handleEvent() {
     self.doSomething()
   }

   subscribe("eventName", block: handleEvent)
}

deinit {
    unsubscribe("eventName")
} 

Will this create a retain cycle to self / my ViewController? And if so, how can I get around it? If I was using a closure, I could use [weak self], but since I'm passing a function, is there anyway to use a [weak self] equivalent?

Roland Rabien
  • 8,750
  • 7
  • 50
  • 67
  • That question is about closures, I'm asking about using functions as closures. Can't use weak self with a function. – Roland Rabien Dec 20 '17 at 17:44
  • Read your code wrong...my bad. just a sec – GetSwifty Dec 20 '17 at 17:58
  • 1
    We need to know what `subscribe` does with the passed closure in order to determine whether or not you have a retain cycle. If it just calls it without storing it or capturing it in another closure that's then stored (on `self` or something that `self` has a strong reference to), then there's no retain cycle. If it does store/capture, then yes you have a retain cycle. It would be most helpful if you could boil down your code to an [mcve]. – Hamish Dec 20 '17 at 18:35
  • "I'm having trouble tracking down a retain cycle" What does Instruments tell you? – matt Dec 20 '17 at 19:03

2 Answers2

3

Long story short, your code does retain a reference. (handleEvent->viewDidLoad->self), http://blog.xebia.com/function-references-in-swift-and-retain-cycles/ has some general strategies to avoid the issue. My recommendation would be to create a function reference, rather than declaring a function:

let eventHandler: () -> () = { [weak self] in 
    self?.doSomething() 
}
subscribe("eventName", block: eventHandler)
GetSwifty
  • 7,568
  • 1
  • 29
  • 46
  • 1
    This is essentially what I was going to say. The answer to the OP's syntax question is that anonymous functions (including function assigned as var value) allows the capture list syntax, and named function declaration does not. But whether the OP's situation would actually cause a retain cycle is impossible to say; the OP has not provided sufficient information. It depends on what `subscribe` does and what the fate of its `block` parameter is (as Hamish has rightly pointed out) — and the OP has concealed that information. – matt Dec 20 '17 at 18:59
  • @matt it's clear that it's event handling and needing to unsubscribe in deinit infers the event could/should fire after the instance is gone. Storing a function reference would stop this from ever happening. – GetSwifty Dec 20 '17 at 19:13
  • @GetSwifty the link is broken. – LGP May 05 '22 at 08:25
3

If you reference a property or method from inside your class it'll create a retain cycle.

class SomeClass {
  val a: (block: (() -> ()) -> ()) = ...

  func run() {
     func b() {
        print("Hello, World!")
     }

     func c() {
        self.someMethod()
     }

     func d() { [weak self] 
        self?.someMethod()
     }

     a(block: b) // no retain cycle
     a(block: c) // retain cycle
     a(block: d) // no retain cycle
  }

  func someMethod() {
     print("Hello, World!")
  }
}
Antlip Dev
  • 651
  • 6
  • 9