On Thu, 1 Sep 2022 17:38:39 GMT, Nir Lisker <nlis...@openjdk.org> wrote:

>> Edit: I missed some nuance, below is the correct version.
>> 
>> There are three objects involved here: the parent observable, the new 
>> observable and the conditional.
>> 
>> What `when` should achieve is to make the new observable and conditional 
>> easy to garbage collect when the conditional is `false` (the parent 
>> observable is considered to be long lived so GC doesn't really apply there). 
>>  The conditional and new observable refer to each other so they must have a 
>> similar life cycle.
>> 
>>> What happens if they are GC'd and the conditional becomes `true` later?
>> 
>> This can't happen, the conditional refers to the new observable, their life 
>> cycles are tied to each other.
>> 
>> Strong references look like this when conditional is `true`:
>> 
>>       conditional <--> new observable <--> parent observable
>> 
>> When it is `false`:
>> 
>>      conditional <--> new observable --> parent observable
>> 
>> Conditional must have a similar life cycle as new observable if your purpose 
>> is to use `when` to break strong references to allow for GC.
>
> If I have a (dumb) method
> 
> 
> void someMethod(ObservableValue<String> longLivedProperty) {
>     ObservableValue<Boolean> condition = new SimpleBooleanProperty(true);
>     ObservableValue<String> whenProperty = longLivedProperty.when(condition)
> }
> 
> 
> then shouldn't `condition` and `whenProperty` be eligible for GC even when 
> `condition` holds `true`? If not, do I not get a memory leak because I can't 
> change `condition` to `false` to allow garbage collection?

Well, this specific example, yes, they'll be both eligible for GC, but that's 
because you're not observing `whenProperty` anywhere. When not observed, it 
won't create a listener on `longLivedProperty`.

Assuming that `whenProperty` is observed and with `condition` being always 
`true` it is basically the same as:

     ObservableValue<String> whenProperty = longLivedProperty.map(x -> x);

As long as `whenProperty` is observed, `longLivedProperty` has a direct 
reference to it (via listener).  With `when` however, the `condition` can be 
used to (temporarily or permanently) break this reference. You could set it to 
`false` in `dispose` for example, triggering the unregistration of the listener 
on `longLivedProperty` (for multiple properties at once if desired):

      ObservableValue<Boolean> active = new SimpleBooleanProperty(true);

      MySkin() {
           
getSkinnable().textProperty().when(active).addListener(this::updateSkinStuffs);
           
getSkinnable().fontProperty().when(active).addListener(this::updateSkinStuffs);
           
getSkinnable().wrapTextProperty().when(active).addListener(this::updateSkinStuffs);
      }

      void dispose() {
           active.set(false);  // kills all listeners/bindings/etc in a single 
line
      }

Or the other case I really like:

     label.textProperty().bind(longLivedProperty.when(label::isShownProperty));

As long as `label` is showing, it takes updates from `longLivedProperty`; if 
the screen is closed, it detaches, and (assuming all such bindings are nicely 
detached) the label (and the rest of the UI it may be part of) can be GC'd 
without problem.

If you kept a reference to the UI (containing that label), then you also kept a 
reference to the label, to its text property, and to that binding with then 
`when` in it -- making that UI visible again restores the listener on 
`longLivedProperty` and the label immediately receives whatever the latest 
value is in that property if it changed while the UI was invisible.

-------------

PR: https://git.openjdk.org/jfx/pull/830

Reply via email to