Связывание объектов не работает вне метода инициализации контроллера FXML - PullRequest
0 голосов
/ 27 апреля 2019

Основная проблема : Binding Слушатели аннулирования объектов не запускаются, если связанные свойства обновляются вне метода initialize(), в котором они объявлены.

Взять этот initialize() метод, объявленный в классе контроллера JavaFX UI:

@FXML
private void initialize() {
    final StringProperty stringProperty = textField.textProperty();
    stringProperty.addListener(
            (observable, oldValue, newValue) -> System.out.println("stringProperty value: " + newValue));
    Bindings.createStringBinding(() -> "PREFIX - " + stringProperty.getValue(), stringProperty).addListener(
            (observable, oldValue, newValue) -> System.out.println("StringBinding value: " + newValue));

    // Editing stringProperty value inside initialize() method
    stringProperty.setValue("u");
    stringProperty.setValue("ua");
    stringProperty.setValue("ua");
    stringProperty.setValue("uaa");
}

Как вы можете видеть, я объявляю StringBinding, который зависит от свойства text TextField, называемого stringProperty, и ChangeListener, который запрашивает вычисление StringBinding, когда оно становится недействительным.

Если я редактирую значение stringProperty внутри метода инициализации, запускаются и stringProperty, и StringBinding прослушиватели изменений, в то время как при редактировании значения stringProperty из пользовательского интерфейса запускается только stringBinding прослушиватель изменений.

Может кто-нибудь объяснить мне, почему это происходит?

1 Ответ

0 голосов
/ 27 апреля 2019

Поскольку строгих ссылок на StringBinding, созданных Bindings.createStringBinding, не существует, в конечном итоге происходит сбор мусора. Как только это произойдет, добавленный слушатель будет собирать мусор вместе с ним.

Я не думаю, что в этом суть, потому что Binding объект прослушивает их зависимости (Observable объекты) с помощью InvalidationListener, а документация Obsevable.addListener(InvalidationListener) гласит, что "The Observable хранит сильную ссылку на слушатель, который предотвратит сборщик мусора и может привести к утечке памяти. "

Это правда, но взгляните на реализацию слушателя, которую используют классы XXXBinding:

/*
 * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package com.sun.javafx.binding;

import java.lang.ref.WeakReference;

import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.WeakListener;
import javafx.beans.binding.Binding;

public class BindingHelperObserver implements InvalidationListener, WeakListener {

    private final WeakReference<Binding<?>> ref;

    public BindingHelperObserver(Binding<?> binding) {
        if (binding == null) {
            throw new NullPointerException("Binding has to be specified.");
        }
        ref = new WeakReference<Binding<?>>(binding);
    }

    @Override
    public void invalidated(Observable observable) {
        final Binding<?> binding = ref.get();
        if (binding == null) {
            observable.removeListener(this);
        } else {
            binding.invalidate();
        }
    }

    @Override
    public boolean wasGarbageCollected() {
        return ref.get() == null;
    }
}

Как вы можете видеть, экземпляр слушателя, который добавляется к зависимостям (то есть Observable s), является WeakListener и поддерживает только слабую ссылку на Binding. Это позволяет Binding собирать мусор, даже если он не был утилизирован должным образом. Это сделано для предотвращения утечки памяти в случае, если Binding выпал из области видимости, а Observable s - нет.

Другими словами, Observable поддерживает сильную ссылку на InvalidationListener, но InvalidationListener поддерживает слабую ссылку на Binding.


Такое поведение задокументировано в «удаленных» местах, включая Property#bind(ObservableValue):

Создайте однонаправленную привязку для этого Property.

Обратите внимание, что в JavaFX все вызовы связывания реализованы через слабых слушателей. Это означает, что привязанное свойство может быть собрано мусором и остановлено для обновления.

А Binding#dispose():

Сообщает Binding, что он больше не будет использоваться и любые ссылки могут быть удалены. Вызов этого метода обычно приводит к остановке привязки для наблюдения его зависимостей путем отмены регистрации его слушателей. Реализация является необязательной.

Все привязки в нашей реализации используют экземпляры WeakInvalidationListener, что означает, что привязку обычно не нужно удалять. Но если вы планируете использовать ваше приложение в средах, которые не поддерживают WeakReferences, вы должны избавиться от неиспользованных Binding s, чтобы избежать утечек памяти.

Примечание. В реализации, похоже, не используется WeakInvalidationListener, но эффект тот же.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...