dart-lang / native

Dart packages related to FFI and native assets bundling.
BSD 3-Clause "New" or "Revised" License
114 stars 40 forks source link

Provide guidance to avoid retain cycles with Obj-C #1510

Open stuartmorgan opened 2 weeks ago

stuartmorgan commented 2 weeks ago

Weak references are an extremely common pattern in Objective-C for avoiding retain cycles or global retention, and it's not at all clear to me how to avoid leaks in the corresponding Dart code.

For instance, if I want to register an observer using addObserverForName:object:queue:usingBlock: the common pattern—indeed, Apple's docs recommend this—is to have self be a weak reference in the block. Then dealloc can make the required call to removeObserver:, and there's no risk of leaking an object. ~There's no way that I'm aware of to make a weak reference in Dart, which means I need to instead find some explicit point to remove that observer, and if I don't, or if I mess up and there are cases where it's not called, then I leak the object forever, because there's a retain that Dart doesn't know about or control.~

~If we don't have some way of replicating the weak reference pattern in Dart, then Dart-Obj-C-FFI code is going to be more error prone than the corresponding Obj-C code, which is very problematic since we don't want an ecosystem of plugins that are much more likely to have leaks than we would without ffigen. Perhaps objective_c should provide a little native weak-wrapper class that on the native side is nothing but a weak id property, and on the Dart side is a templated class that down-casts the access to the template type?~

Since I'm probably in the majority in not being familiar with WeakReference, we should prominently document how to replicate this pattern in Dart.

(This is essentially the Obj-C version of #575)

HosseinYousefi commented 2 weeks ago

There's no way that I'm aware of to make a weak reference in Dart,

https://api.flutter.dev/flutter/dart-core/WeakReference-class.html

Edit: Although I'm not sure if this works out of the box with the obj-c object type.

stuartmorgan commented 2 weeks ago

Ah, right 🤦🏻 I have actually encountered that once (in the wrapper generator code), but forgot it existed.

Updated to make this a docs request rather than a functionality request.

Edit: Although I'm not sure if this works out of the box with the obj-c object type.

I don't know that it really has to. The usual pattern is a weak reference to self, and in the context of Dart FFI code, self (i.e., this) is going to be a regular Dart object.

liamappelbe commented 2 weeks ago

@stuartmorgan If you try out WeakReference, let me know if solves this case. My hunch is that it won't actually work, and we're going to need something specific in package:objective_c.

stuartmorgan commented 2 weeks ago

WeakReference does solve my use case.

In retrospect, the now-crossed-out suggestion in my initial post wouldn't have worked, because the use case I have isn't wiring up a weak reference to a native object, it's needing a weak reference to a Dart object in a block that's ultimately owned by native code. The Dart->Native weak reference case could exist, so maybe we'll still need that, but the common case that I was thinking of is solved by WeakReference.