栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

如何将侦听器添加到双向绑定的对象

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

如何将侦听器添加到双向绑定的对象

您的绑定和

toBind
属性正在被垃圾回收。

托马斯·米库拉(Tomas Mikula)在他的博客上简要地描述了“过早的垃圾收集”问题。


首先,对于任何试图重现此问题的人来说,请速记一点。由于所描述的行为取决于发生的垃圾回收,因此它可能并不总是发生(取决于内存分配,所使用的GC实现以及其他因素)。如果添加行

root.setonMouseClicked(e -> System.gc());

对该

start()
方法进行操作,然后单击场景中的空白区域将请求进行垃圾回收,并且该问题将(至少更有可能)在此之后(如果尚未出现)显现出来。


问题是绑定使用

WeakListener
s侦听属性更改并将这些更改传播到绑定的属性。如果没有其他活动引用,弱侦听器的设计目的是不要阻止对其附加的属性进行垃圾回收。(其基本原理是避免在属性不再在范围内时强迫程序员强制清理绑定。)

在您的示例代码中,控制器及其属性

toBind
可以进行垃圾回收。

在之后

start()
方法完成,所有你都保证有被引用
Application
,当你调用创建的实例
launch()
中,
Stage
结果表明,和任何从这些引用。当然,这包括
Scene
(所引用的
Stage
),的
root
,的子代
root
,其子代等,这些的属性,以及这些属性中任何一个的(非弱)侦听器。

因此,

stage
对的引用是
scene
,对的引用是,
GridPane
这是其根,对的引用也对
textarea

textarea
有附加到它的侦听器的引用,但听者保持没有额外的引用。

(在代码的第二个版本中,

ChangeListener
附加到的非弱
textarea.textProperty()
引用有对的引用
toBind
。因此,在该版本中,
ChangeListener
禁止
toBind
将GC进行GC,然后您可以在其上看到侦听器的输出。)

加载FXML时,将

FXMLLoader
创建控制器实例。虽然该控制器实例具有对string属性和文本区域的引用,但事实并非如此。因此,一旦加载完成,就没有对控制器的实时引用,并且可以
StringProperty
对其进行定义的垃圾回收。文本区域
textProperty()
仅对上的侦听器具有
弱引用
toBind
,因此文本区域无法防止
toBind
被垃圾回收。

在大多数实际情况下,这将不是问题。

StringProperty
除非您打算在某处使用它,否则您不太可能创建此附加对象。因此,如果您添加以“自然”方式使用此代码的任何代码,则很可能会看到问题消失。

因此,例如,假设您添加了一个标签:

<Label fx:id="label" GridPane.rowIndex="1"/>

并将其文本绑定到属性:

  public void initialize() {    textarea.textProperty().bindBidirectional(toBind);    textarea.textProperty().addListener((observable, oldValue, newValue) -> {      System.out.print("textarea: ");      System.out.println(newValue);    });    toBind.addListener((observable, oldValue, newValue) -> {      System.out.print("toBind: ");      System.out.println(newValue);    });    label.textProperty().bind(toBind);  }

然后场景中有对标签的引用,依此类推,因此不进行GC处理,并且标签

textProperty
通过绑定到来具有弱引用
toBind
。由于
label
不是GC,因此弱引用
toBind
可以在垃圾回收中幸存,并且不能进行GC,因此您将看到期望的输出。

或者,如果您在

toBind
其他地方(例如在
Application
实例中)引用该属性,则:

public class Controller {  @FXML  textarea textarea;  private StringProperty toBind = new SimpleStringProperty();  public void initialize() {    textarea.textProperty().bindBidirectional(toBind);    textarea.textProperty().addListener((observable, oldValue, newValue) -> {      System.out.print("textarea: ");      System.out.println(newValue);    });    toBind.addListener((observable, oldValue, newValue) -> {      System.out.print("toBind: ");      System.out.println(newValue);    });  }  public StringProperty boundProperty() {      return toBind ;  }}

然后

package sample;import javafx.application.Application;import javafx.beans.property.StringProperty;import javafx.fxml.FXMLLoader;import javafx.scene.Parent;import javafx.scene.Scene;import javafx.stage.Stage;public class Main extends Application {    private StringProperty boundProperty ;    @Override    public void start(Stage primaryStage) throws Exception{        FXMLLoader loader = new FXMLLoader(getClass().getResource("sample.fxml"));        Parent root = loader.load();        Controller controller = loader.getController();        boundProperty = controller.boundProperty();        root.setonMouseClicked(e -> System.gc());        primaryStage.setScene(new Scene(root, 400, 300));        primaryStage.show();    }    public static void main(String[] args) {        launch(args);    }}

您会再次看到预期的行为(即使在垃圾回收之后)。

最后(最后一点很微妙),如果您用

textarea.textProperty()
匿名内部类替换侦听器:

textarea.textProperty().addListener(new ChangeListener<String>() {  @Override  public void changed(Observablevalue<? extends String> observable, String oldValue, String newValue) {    System.out.print("textarea: ");    System.out.println(newValue);  }});

那么这也会阻止的GC

toBind
。原因是匿名内部类的实例包含对封闭实例的隐式引用(在这种情况下,即控制器的实例):此处控制器保留对的引用
toBind
。相比之下,Lambda表达式则不这样做。



转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/464354.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号