djinni
djinni copied to clipboard
ObjcProxy interacts poorly with weak references
I have an API that has an interface +j +o object (call it "Platform") with an interface +c delegate (call it "Delegate") that are going to want to talk to each other. The easiest and most natural way to break the refcount cycle is to have Platform retain a weak reference to Delegate, since a Platform matters exactly as long as a Delegate is interested in it.
But I can't do this since Platform's reference to Delegate is actually to a Djinni proxy that nobody else retains a reference to.
An alternative would be to have some mechanism of keeping track of all the Platforms that currently matter, and some way of notifying Delegates when platforms go out of scope (I suppose a did_finish method or something), but this is adding needless complication to the API.
I'd love to hear about other alternatives.
The way we solve this is to split the Delegate up in two where one half is registered with Djinni and it itself has a weak reference to the actual Delegate that is doing the real work.
In our case it's typicall the ViewController/Activity owning a +c ViewModel and also a +o+j ViewModelDelegate. Threfore ViewModelDelegate is its own type and holds a weak reference to the ViewController/Activity to break the cycle. If ViewController/Activity implemented the ViewModelDelegate interface there would be a ref cycle and it would need manual breaking in viewDidUnload or onDestroyed.
So you solve this with ViewController (strong) -> ViewModel, ViewController (strong) -> ViewModelDelegate, ViewModelDelegate (weak) -> ViewController. And I suppose ViewModelDelegate (weak) -> ViewModel?
ViewController (strong) -> ViewModel ViewModel (strong) -> ViewModelDelegate ViewModelDelegate (weak) -> ViewController
The ViewController does not require a reference to the Delegate and the Delegate does not need to know about the ViewModel, it's only job is forwarding calls to the ViewController because in a "typical" OC-only world one would have the ViewController implement the ViewModelDelegate protocol and the reference to the delegate is weak, but that's not possible here. So if the ViewModel owner implements the ViewModelDelegate it has to break the cycle manually. That's why we have an additional object inbetween. So when the ViewController dies it takes the ViewModel with it, resulting in the Delegate's death as there are no other refs to it, and if there are, then the weak pointer is nil and all calls get dropped.
In Java the whole thing works the same except weak references in Java are a bit more tedious to work with.
Fair enough. A solution similar to that but on the C++ side turns out to work for my case: Owner has a strong ref to Platform and Delegate, Platform has a strong ref to Delegate, Delegate has a weak ref to Platform. Since Owner doesn't need to do anything to Platform aside from break the refcount cycle, I used something sort of like this to hold the ref:
https://gist.github.com/mrdomino/5cd01e67b6ccf9498433
In Java the whole thing works the same except weak references in Java are a bit more tedious to work with.
Can anyone tell me if using a weak reference is necessary in Java in the case that mknejp describes? It appears to me that the system has a simple reference cycle that the GC is going to detect and handle appropriately.
EDIT: I think the answer to my question is that the GC would have to know about the pointers in the C++ portion of the code in order to see the cycle. A weak reference is necessary because the GC is not aware of the pointers in the C++ portion of the code.