翻車了!!!一個小例子帶你瞭解閉包。事故現場:場景:6個button,上方1個text。點擊button,text會顯示button上的數字。代碼如下://在unity裡面賦值public List<Button> buttons = new List<Button>();public Text t ...
翻車了!!!一個小例子帶你瞭解閉包。
事故現場:
場景:6個button,上方1個text。點擊button,text會顯示button上的數字。
代碼如下:
//在unity裡面賦值
public List<Button> buttons = new List<Button>();
public Text text;
void Start () {
AddFun();
}
//給6個button添加點擊事件
void AddFun()
{
for (int i = 0; i < buttons.Count; i++)
{
buttons[i].onClick.AddListener(() => Fun(i + 1));
}
}
//點擊事件:輸出形參的值
void Fun(int i)
{
text.text = i.ToString();
print(i);
}
for迴圈button添加onclidk函數導致點擊所有的button顯示同樣的莫名數字7。
事故原因:
閉包。標題那麼長跟閉包有什麼關係?因為上述問題會引起閉包。為什麼標題中不直接寫出關鍵字閉包?因為叫的出閉包的人基本上都會避免這個問題,這篇文章主要針對新人,那些沒聽過閉包的人。
事故分析:
1、什麼是閉包?我也不知道哈,非科班,定義百度一下吧。
2、怎麼會產生閉包?內部函數使用了外部函數的局部變數。內部函數:是我對匿名函數,lambda表達式等函數嵌套情況的稱呼。如上述例子:外部函數Addfun,內部函數是lambda表達式,i是外部函數for迴圈中的局部變數,內部函數lambda表達式使用了i這個局部變數。
3、閉包會導致什麼的結果?閉包會延長所用外部函數局部變數的生命周期,或者說是局部變數變成了全局變數。
記憶體分析:
通俗的講就是局部變數i從沒被釋放,一直存在記憶體中,隨著i++,值增到了6,所有button的點擊函數都是傳進的i+1,也就是7,為實參,所以fun函數列印的數字為7
處理辦法:
重新申請記憶體。代碼如下:
public List<Button> buttons = new List<Button>();
public Text text;
void Start () {
AddFun();
}
void AddFun()
{
for (int i = 0; i < buttons.Count; i++)
{
//重新申請的記憶體
int index = i;
buttons[i].onClick.AddListener(() => Fun(index + 1));
}
}
void Fun(int i)
{
text.text = i.ToString();
print(i);
}
記憶體分析:
通過初始化重新申請了6塊記憶體,內塊記憶體保存了不同的index值(index值是由i賦值而來)。雖然index變數的生命周期也被延長,但是不同的button的點擊函數訪問的傳進參數的記憶體不同,因此fun函數列印的數字也就不同。
事件解決:
拓展:
其實閉包在腳本語言中非常常見,也非常有用。以lua為例,代碼如下:
function OutSideFun()
local i=0
local function InsideFun()
i=i+1
print(i)
end
return InsideFun
end
local fun=OutSideFun()
fun()
fun()
fun()
那麼自己可以試試輸出值是多少,分析一下為什麼是這樣。
更多unity2018課程請直接到paws3d上查找。