我实例化了许多按钮,这些按钮在单击时需要调用一个函数(通过侦听器)。但是我也经常摧毁它们。然后这些听众也被销毁了吗?还是我需要删除它们?
示例:
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
被销毁时,此侦听器会保留吗?我正在实例化和删除大量按钮,因此它可能对我来说很重要。
答案 0 :(得分:6)
好吧,这将变得更深。
考虑一下:
Button button;
void Start()
{
button.onClick.AddListener(Method);
}
void Method()
{
print("Hey");
}
这很好,您的Button组件将获得“方法”的“链接”,因此在触发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);
在使用实例调用时,此参数移到最前面,并且变为非强制性。您可以在方法中实际使用此参数。同样,这不是强制性的。
因此,在第一种情况下,尽管脚本不再存在,但它仍然可以工作,这是因为没有使用任何实例成员。在最后一个示例中,使用了str,并且由于该对象不再存在,因此它将引发null引用异常。
考虑另一种方式。
如果创建脚本仍然存在,并且Button游戏对象或组件被破坏,那么您完全没有风险。清除onClick事件会更明智,但由于它将被销毁,因此可以采用其他方法。在创建所有按钮的循环的下一次迭代中,您的生成器会失去对Button的任何了解:
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
答案 1 :(得分:1)
我发现您的答案here更准确
我可能会误解文档的措辞,但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值,则会得到它?尽管检查员显示Missing(Type)?? !! ......
这告诉我,如果程序员在没有证明是错误的情况下没有手动删除侦听器,那么这里就会发生内存泄漏。显然。
所以...。是和否。根据文档,它已被销毁,检查员说它已销毁,但仍在内存中……并且可以调用。
edit 有一个RemoveListener()方法,但它似乎什么也没做,只是放慢了一切,我已经看到它引发了一些奇怪的错误。除非您真的担心性能,否则请期望unity尽最大努力在onDestroy()上删除它们,而不必为此担心太多...