aleclarson / emitter-kit

Type-safe event handling for Swift

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Prevent retain cycles

yvbeek opened this issue · comments

Working with the Event class I often have to make sure that I'm not creating a retain cycle.
To make it a bit easier, I've created the following helper:

func on<Owner: AnyObject>(_ owner: Owner, _ method: @escaping (Owner) -> (T) -> Void) -> EventListener<T> {
  return self.on { [weak owner] data in
    if let ownerRef = owner {
      method(ownerRef)(data)
    }
  }
}

You can use it like this:

var listener = myEvent.on(self, MyClass.onMyEvent)

private func onMyEvent(data: Data) {
  print(data)
}

Would you consider adding this to the codebase?

Perhaps the method should have a different name than on to prevent confusion with the overloads that have the targetting arguments.

I'm a little confused about your use case here.

If the owner is retaining listener (and the listener is referencing the owner), you'll want to use [weak owner] to prevent a retain cycle.

Let me know if that doesn't help. Thanks!

It is actually a sort of poor-man's delegate implementation.

With the on function you risk creating a retain cycle if you use self, because self retains the listener and the block is retaining self (if you don't use weak).

One option is to do this:

self.listener = myEvent.on { [weak self] data in {
  guard let me = self else { return }
  ...
}

If your handler is a bit more complicated, and you move it to a function, some people might do this:

self.listener = myEvent.on(onMyEvent)

private func onMyEvent(data: Data) {
  print(data)
}

Which is really risky, because you are actually creating a retain cycle, but it isn't that visible.

In Swift you can call a instance function statically by providing self as the first argument. My helper makes it easier to call a handler function without risking a retain cycle.

Actually, rather than using [weak self] (weak owner), you should be using [unowned self] here.

listener = myEvent.on({ [unowned self] in self.onMyEvent($0) })

not far from something like:

listener = myEvent.on(owner: self, handler: onMyEvent)

//*or maybe just something like this since the listener is tied to the owner:
myEvent.on(owner: self, handler: onMyEvent)

If your handler is a bit more complicated, and you move it to a function, some people might do this...

I think the people you are talking about would probably still do this even if this library would contain the method you are suggesting to have.

Actually, rather than using [weak self] (weak owner), you should be using [unowned self] here.

listener = myEvent.on({ [unowned self] in self.onMyEvent($0) })

not far from something like:

listener = myEvent.on(owner: self, handler: onMyEvent)

//*or maybe just something like this since the listener is tied to the owner:
myEvent.on(owner: self, handler: onMyEvent)

If your handler is a bit more complicated, and you move it to a function, some people might do this...

I think the people you are talking about would probably still do this even if this library would contain the method you are suggesting to have.

Just a quick note on [unowned self] usage here. It is quite possible that the owner may be nil as in objects that have passed beyond the current frame. I think weak would be a safer route. I do agree that if one is likely to miss the opportunity for a retain cycle then adding more stuff to the library seems unnecessary. With that in mind, I'm not sure why a help wanted tag is still on this 'enhancement'?