天天看點

Unity3d 控件被指派前因控件被删除而導緻的通路空對象錯誤

問題:通過攜程向伺服器請求資料後,通過閉包形式給控件指派,此時如果控件優先于指派方程完成前被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;//指派
		}
    });
}
           

繼續閱讀