当按钮被销毁时,onClick 的侦听器是否被销毁?



我正在实例化许多按钮,单击这些按钮时需要调用函数(通过侦听器(。但我也经常摧毁它们。然后这些侦听器是否也被销毁,或者我是否需要删除它们?

例:

public void makeButton()
{
GameObject spawnedButton = Instantiate(prefabButton, prefabButton.transform.position, prefabButton.transform.rotation) as GameObject;   
spawnedButton.GetComponent<Button>().onClick.AddListener(()=>
{
listedButtonClicked(someOtherObjectThatWillNotBeDeleted, spawnedButton);
});
}
public void listedButtonClicked(GameObject target, GameObject button)
{
Debug.Log(target);
Debug.Log(button);
}

。那么当spawnedButton被摧毁时,这个听众会留下来吗?我正在实例化和删除大量按钮,因此它可能与我相关。

好吧,这会有点深入。

考虑一下:

Button button;
void Start()
{
button.onClick.AddListener(Method);
}
void Method()
{
print("Hey");
}

这很好,您的 Button 组件会获得指向 Method 的"链接",因此当触发 onClick 时,它会跳转到 Method 的地址并从那里运行代码。

第二种情况:

Button button;
void Start()
{
button.onClick.AddListener(Method);
Destroy(this);
}
void Method()
{
print("Hey");
}

请注意,我销毁了当前组件,运行它并触发了onClick,没有问题(??!!(,它打印正常。

第三种情况:

Button button;
string str = "Hey there";
void Start()
{
button.onClick.AddListener(Method);
Destroy(this);
}
void Method()
{
print(this.str);
} 

它崩溃了。现在进行解释。方法始终是静态的,编译器(或创建者(足够聪明,可以考虑每个实例不需要有自己的方法,而是需要一个共享方法模板,实例可以自行传递到该模板。

方法如下:

void ClassName.MethodName(this ClassName); 

使用实例调用时,this 参数将移到前面,并且不是必需的。此参数实际上在方法中可用,因为您可以使用它。同样,这不是强制性的。

因此,在第一种情况下,尽管脚本不再存在,但它仍然有效,这是因为没有使用任何实例成员。在最后一个示例中,使用了 str,由于对象不再存在,因此会引发空引用异常。

反过来考虑。

如果创建脚本仍然存在,并且 Button 游戏对象或组件被销毁,那么您完全没有风险。清除onClick事件会更明智,但由于它将被销毁,因此其他方式。您的生成者在创建所有按钮的循环的下一次迭代中会丢失任何关于按钮的知识:

for(i=0;i<10;i++)
{
GameObject go = null; // new reference added on method stack
go = Instantiate<GameObject>(btnPrefab); // new instance added to reference
} // go connection is lost right here, a new go added to stack in next iteration

我发现你的答案更准确

我可能误解了文档的措辞,但 AddListener 旨在添加一个非持久侦听器。

但是,下面显示了一个奇怪的行为(至少在我得到解释之前(:

private float myFloat = 10f;
[SerializeField] private Button button = null;
void Start( )
{
button.onClick.AddListener(MyMethod);
DestroyImmediate(this.gameObject);
}
public void MyMethod()
{
Debug.Log("Call " + this.myFloat);
this.myFloat ++;
}

最好的部分是对象肯定消失了,this.myFloat仍然被打印和增加,我没有任何持久的监听器。

如果我在销毁中删除,那么一切都很好。似乎 AddListener 正在创建指向内存中对象的链接,即使触发 GC 也不会收集该对象。我认为使用弱引用可能会使其恢复生机。

更好的是,如果我在上面创建对脚本的引用并调用 myFloat 值,我就会得到它??!!尽管检查器显示缺失(类型(??!!...

这告诉我,如果程序员不手动删除侦听器,我们这里就会出现内存泄漏,直到我被证明是错的。 显然。

所以。。。。是和不是。根据文件,它被摧毁了,检查员说它被摧毁了,但它仍然在记忆中......并且可以调用。

编辑有一个 RemoveListener(( 方法,但它似乎什么也没做,只是减慢了一切,我已经看到它抛出了一些奇怪的错误。 除非你真的担心性能,否则期望 Unity 在 Destroy(( 上尽最大努力删除它们,不要太担心它......

相关内容

最新更新