Unity-按钮被销毁时,onClick的监听器是否被销毁?

时间:2018-09-19 19:21:28

标签: c# unity3d unityscript

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

示例:

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被销毁时,此侦听器会保留吗?我正在实例化和删除大量按钮,因此它可能对我来说很重要。

2 个答案:

答案 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()上删除它们,而不必为此担心太多...