1. 關于lua函數傳參參數
在lua中給function傳遞參數的時候一般分為兩種情況:值傳遞和引用傳遞
值傳遞:
值傳遞:數字、字符串、布爾值、nil等基本類型通過值傳遞。函數內部接收的是外部變量的副本,修改副本不會影響原始變量。
雖然我們都知道字符串是引用類型存儲的數據,但是在函數傳參的時候傳遞的是指向字符串內存地址的指針值?(即引用的副本),而非原始變量的引用本身
function Test(n)n = n + 1
end
local a = 1
Test(a)
print("輸出a:"..a)
-- 結果a仍然是1
引用傳遞:
?表(table)和函數如果當作函數的參數傳遞時,傳遞的時指向具體變量的地址的副本,記住時副本!!可以理解成表本身就是一個指針,把這個指針當作參數傳遞給函數時,會重新生成一個指針,并且把該變量的指針地址值復制給新生成的指針。所以這種情況下,如果直接操作(增刪)都可以正常影響指向的變量數據。但是如果時賦值操作,那么就會修改傳遞過去的副本指向的內容。
function Test1(t)t.a = 1
endfunction Test2(t)t = {a = 15,b =3}
endfunction Test3(t)t = nil
endlocal t = {a = 10, b = 2}Test1(t)
print("t.a:"..t.a.." t.b:"..t.b)
Test2(t)
print("t.a:"..t.a.." t.b:"..t.b)
Test3(t)
print("t.a:"..t.a.." t.b:"..t.b)
上述代碼三次的輸出都是: t.a:1 t.b:2,
.
調用Test1的時候直接修改了引用指向的內容,所以修改生效。此時外部的t.a = 1。
.
調用Test2的時候直接把引用副本指向了另一個新表的內存,那么這時候函數內部的t已經和外部傳進去的t沒有關系了,所以怎么修改都不會對外部的t造成影響。
.
調用Test3的時候和Test2一樣,把引用副本指向了空,同樣和外部的t沒有關聯關系了,所以外部t不受影響。
2. 關于c#和lua相互引用(xlua版)
userdata
我們都知道lua中有一種數據類型-userdata
.
userdata 是 Lua 為存儲 C/C++ 等外部語言數據而設計的特殊類型,提供了一塊原始內存區域,可直接存放結構體、指針、文件句柄等外部對象。它允許 Lua 通過指針或內存塊直接操作外部環境的數據,是 Lua 與 C/C++ 交互的核心機制。例如,C 中定義的 struct People 可通過 userdata 傳遞給 Lua 腳本使用.
.
所以一個語言只要可以和c/c++交互,那么就可以通過userdata和lua交互,這也是c#和lua交互的底層原理。
.
xlua中的c#和lua交互
在xlua中,c#和lua的交互都是通過c這個他們兩種語言都能到的媒介實現的。
大致流程如下:
lua call c#
lua調用c# -> 調用c -> 操作虛擬棧對參數壓棧 -> 調用c#wrap函數/反射查找函數 ->
把lua函數參數轉換成c#類型 -> 完成調用
c# call lua
c#調用lua -> c#調用c函數 -> 通過capi找到lua函數(lua_getglobal) -> c#數據轉成lua數據(lua_pushinteger) ->
通過capi調用lua函數(lua_pcall)
xlua中c#和lua相互引用
在xlua框架中,可以很方便的在lua中持有C#的對象變量并使用,反之一樣。
lua持有c#對象:
通過上述的userdata了解,我們可以大致了解到,其實c#的變量在lua中是以userdata類型的數據存儲的。
事實上在lua中從來沒有真正的持有c#的真正變量。
?C#對象在Lua中的表示
在xLua中,C#對象傳遞到Lua時會被封裝為userdata類型。這個userdata并不直接存儲C#對象本身,而是存儲一個唯一標識符(objId)?,用于關聯C#端的實際對象
引用關系的管理(lua持有#)
但是我們也知道c#的gc是并不清楚lua環境中該userdata的引用情況的,如果直接不管lua中是否引用只要c#段就沒有引用就GC掉顯然不符合我們的需求。所以xlua中設計了ObjectTranslator.cs。
ObjectTranslator通過ObjectPool和ReverseMap維護對象與objId的映射,確保C#對象不被GC回收(只要Lua仍持有userdata)
而在lua中,userdata是被存放在弱引用表中的,只要lua中沒有強引用該對象的存在,那么該userdata就可以被lua的gc回收掉,防止lua一致持有著objid而c#無法回收對應對象。
lua對象在c#中的表示:
所有Lua對象在C#中通過LuaBase基類管理,其構造函數調用luaL_ref在Lua注冊表中創建引用,析構時調用luaL_unref釋放
其實講了那么多,就是為了理解如下代碼:
public class Main{public class TestClass{}public static TestClass MyClassTest;public static TestClass CreateTestClass(){if (MyClassTest == null) MyClassTest = new TestClass();return MyClassTest;}public static void DestroyTestClass(){MyClassTest = null;}
}
local myClass,myClass2,myClass3local function Execute()myClass = CS.Main.CreateTestClass()myClass2 = CS.Main.MyClassTestmyClass3 = myClass2myClass2 = nilcollectgarbage("collect")CS.System.GC.Collect()CS.Main.DestroyTestClass()collectgarbage("collect")CS.System.GC.Collect()
endpcall(Execute)
由上面講述我們可以知道,一個c#變量在lua中的話,兩端是各自持有各自的類型(userdata和TestClass),上述lua代碼中的myClass,myClass2和myClass3就是對應的userdata。
.
即使myClass2 = nil后,myClass3和myClass仍然持有該userdata,所以第一次執行雙方gc,只會回收掉myClass2。myClass3和myClass以及c#段的MyClassTest都沒收影響。
.
那么再來看CS.Main.DestroyMyClass() 這句之后c#側的MyClassTest引用被置空,注意只是引用實際變量還被lua持有,所以即使執行了C#的gc,真正的對象仍不會被回收掉,因為myClass 和myClass3仍然還存在著并且持有著。
.
那么這時候如何回收掉TestClass對應的數據,只需要再加上
myClass2 = nil
myClass = nil
collectgarbage("collect")
CS.System.GC.Collect()
這樣的話lua測得強引用也沒了,都可以被回收了