問題:通過攜程向伺服器請求資料後,通過閉包形式給控件指派,此時如果控件優先于指派方程完成前被destroy,會報錯“嘗試給一個被destroy物體指派”,次問題可能引起軟體崩潰(特别是移動端)
示例代碼如下:
private void GetBalance()//調用方程
{
for (int i = 0; i < m_curItems.Count; i++) //m_curItems存儲了一堆控件對象
{
m_curItems[i].getBalance();//調取控件對象自己的函數
}
}
[SerializeField]private Text balance; //控件綁定
public void getBalance()//請求資料,閉包内指派
{
//此SDK資料請求方式為協程
MarvelSDK.SDK.API.GetETHBalance(m_detail.addr, (error,x) =>
{
if (x != null)
{
balance.text = x.balance;//指派
}
});
}
解決思路:
1. 控件删除時,停止所有協程 StopAllCoroutines();
2. 添加判斷條件,判斷控件是否還存在,如果存在則指派,不存在則不進行操作
嘗試debug時所遇到的問題:
1. 雖然停止了資料請求方程的協程,但是由于采用閉包方式在給控件指派,因閉包特性,是以閉包内指派方程依舊會執行,是以思路1不好使
2. 起先添加判斷條件,嘗試通過判斷此控件是否還存在于場景中來控制指派方程的運作
if(balance.gameObject.activeInHierarchy)
但此時犯了一個判斷錯誤,”activeInHierarchy“是用來判斷控件是否在場景中是否可見,但如果控件被destroy了,那麼此時此方法也會報“嘗試通路被destroy物體”的錯誤。
由此,引出今天的重點,換一個思維來判斷控件是否被destroy。
首先,确定一個基礎概念,一個控件被執行個體化之後,實際上是在記憶體内開辟了一段記憶體空間,我們對控件的操作實際上是對這段記憶體空間的操作,既然是對記憶體空間的操作,也就意味之我們需要一個記憶體指針,而此時我們的記憶體指針其實就是這個預制體綁定參數:
[SerializeField]private Text balance;//這貨可以了解成一個指針,指向它所綁定的控件對象
而且我們知道unity3d中銷毀函數Destroy()的功能是銷毀執行個體,釋放控件所綁定的記憶體區域,重點來了,既然這個控件記憶體區域已經被釋放,那麼指向它的函數指針也會被銷毀,但由于我們我們在閉包中使用了這個指針,因閉包的特性,它會被閉包所生成的匿名類抓取,被匿名類所引用(任何一個函數或參數如果被其他對象所引用,那麼他将不會被銷毀,直到引用它的對象被銷毀時才會一并銷毀)是以依然存在,但由于它所指的控件被destroy了,是以此時這個函數指針會指向null。
是以綜上所述,最終解決這個問題的方式是判斷這個函數指針是否指向null, 代碼如下:
private void GetBalance()//調用方程
{
for (int i = 0; i < m_curItems.Count; i++)
{
m_curItems[i].getBalance();
}
}
[SerializeField]private Text balance; //指向控件的記憶體區域
public void getBalance()//請求資料,閉包内指派
{
MarvelSDK.SDK.API.GetETHBalance(m_detail.addr, (error,x) =>
{
if (x != null)
{
if(balance == null )//如果指向null,意味着控件被删除,則強制傳回上層函數
return;
balance.text = x.balance;//指派
}
});
}