NGUI 3.0 之后 采用了 一種 全新的 事件系統 List<EventDelegate> ,使用起來並不麻煩。
但是最近做項目碰到一個小問題,特此分享一下。
PS NGUI3.6.4版本
PS 有空還是看看 源碼,有些 時候 光理解 還是沒用的。
一、問題出現在UI 邏輯一塊,在一個 UITween 調用之后 要回調一個 事件。
代碼如下:
1 public class MyTest2 : MonoBehaviour 2 { 3 public UITweener tween; 4 5 6 List<EventDelegate> list; 7 public virtual void Start () { 8 list = tween.onFinished; 9 10 EventDelegate.Add(tween.onFinished, Test); 11 } 12 13 List<string> list1 = new List<string>(); 14 void Test() 15 { 16 Debug.Log(list == tween.onFinished); 17 18 EventDelegate.Remove(tween.onFinished, Test); 19 20 Debug.Log(" Test "); 21 } 22 23 void OnGUI() 24 { 25 if (GUI.Button(new Rect(10, 10, 150, 100), "I am a button")) 26 tween.PlayForward(); 27 if (GUI.Button(new Rect(10, 110, 150, 100), "I am a button2")) 28 tween.PlayReverse(); 29 } 30 31 32 }
但是之前 我是 以為 EventDelegate.Remove 這樣就能夠把 Onfinished 委托給去除的。
結果我發現不是如此,之后 這個 Tween 每次 調用 結束 之后, Test() 都會執行。

二、源碼分析
看了下源碼。發現問題出在這里:
這段代碼就是 tween 結束后 調用的。
這里把 mTemp = OnFinish
再把 原來的OnFinish 指向了 一個 新 的 new 的 list
然后在 mTemp execute 執行完畢 之后 ,再舊瓶裝新酒,把mTemp重新裝進 OnFinish 這個list.

來觀察一下 Add 的 流程
我們可以看到 他 使用了 onFinished 的 list , add 了 一個 包含了 test() Callback 的 EventDelegate

接下里 執行 的的時候 使用了 同樣的 list ,編輯得到 剛才那個 包含了 test() Callback 的 EventDelegate ,然后執行其中的 test()

同樣的 Remove 操作也是如此。

但是 我們可以看到 tween Execute 的 時候 調用 原來 增加進來的 Test 函數,
可是在 Test 函數 中 這個時候引用 的 onFinished 已經 變過了。成為了一個新的 new list.並且還沒有把 原來的 委托放進來。
所以這樣一來 , 即使你調用 EventDelegate.Remove , 因為 onFinished list.cout == 0 , 沒有 remove test()
也就意味着 tween 結束后 會永遠調用 test() 。因為在 tween Execute 之后,onFinish 這個 list 才會加入 原來 所有的 委托,這個時候 list.cout 才是1. 重新 加入了 test() 這個時候 remove 才有意義。

三、解決方法
但是我感覺 治標 不 治本,如果 我 想 調用 好幾次 再 Remove 呢 ? 以后有空 在弄吧。
其實就是 在 執行的 時候 判斷 一下,oneShot 標志位,執行完畢后mTemp 中 remove test(),再放到OnFisish 的時候 test() 就沒有了。
1 public class MyTest2 : MonoBehaviour 2 { 3 public UITweener tween; 4 5 6 List<EventDelegate> list; 7 public virtual void Start () { 8 list = tween.onFinished; 9 10 //EventDelegate.Add(tween.onFinished, Test); 11 EventDelegate.Add(tween.onFinished, Test, true); 12 } 13 14 List<string> list1 = new List<string>(); 15 void Test() 16 { 17 Debug.Log(list == tween.onFinished); 18 19 //EventDelegate.Remove(tween.onFinished, Test); 20 21 Debug.Log(" Test "); 22 } 23 24 void OnGUI() 25 { 26 if (GUI.Button(new Rect(10, 10, 150, 100), "PlayForward")) 27 { 28 Debug.Log(" PlayForward"); 29 tween.PlayForward(); 30 } 31 32 if (GUI.Button(new Rect(10, 110, 150, 100), "PlayReverse")) 33 { 34 Debug.Log(" PlayReverse "); 35 tween.PlayReverse(); 36 } 37 38 } 39 }
上結果:

