Lua調用c#發生了什么?


目錄:Xlua源碼學習

本篇以CS.XLua.LuaDLL.Lua:xlua_is_eq_str(L,index,str)的調用為例子。

文章比較長,先說結論:

1.CSLuaEnvinit_xlua代碼塊里生成的全局表,核心init_xlua里的metatable:__index方法。

2. XLua:以XLua為類名的類不存在,當做命名空間處理。CS.XLua = { ['.fqn'] = fqn }

3. LuaDLL:同上,CS.XLua.LuaDLL = { ['.fqn'] = fqn }

4. LuaLua類存在,導出類。

   調用StaticLuaCallbacks.ImportType導出類。

ObjectTranslator.TryDelayWrapLoader:調用wrap的__Register方法加載類。

__Register:生成元表obj_meta,該元表封裝了類的所有成員方法、get、set調用接口__index,__newindex。

生成cls_table,該表包含了所有類的靜態方法。

生成cls_meta,cls_table的元表,封裝了靜態的屬性設置、獲取接口__index,__newindex。

設置CS.XLua.LuaDLL.Lua = cls_table。

類導出結束。

5.調用靜態方法

1. 靜態方法獲取:cls_table.staticFunc,直接取。

2. 靜態get方法:通過cls_meta的__index(xlua.c的cls_indexer)獲取,非父類方法存在getters。

3. 靜態父類方法:通過cls_meta的__index(xlua.c的cls_indexer)獲取,實際通過父類的cls_index獲取的,即register["LuaClassIndexs"][base_ud] = base_meta.__index。涉及父類操作下同。

4. 靜態set:通過cls_meta的__newindex(xlua.c的cls_newindexer)獲取,非父類方法存在setters。

6.對象實例獲取:

兩種方式:

1. new一個對象,例如在lua執行UIHierarchy(),會觸發UIHierarchy的cls_meta的__call方法,而__call方法綁定了下面的UIHierarchyWrap .__CreateInstance(),最終是new了一個對象,然后把實例對象的ud壓棧返回,你就可以通過這個ud的元表obj_meta調用到成員方法。

static int __CreateInstance(RealStatePtr L)
{        
    try {
       ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(L);
       if(LuaAPI.lua_gettop(L) == 1)
       {    
          UIHierarchy gen_ret = new UIHierarchy();
          translator.Push(L, gen_ret);
          return 1;
       }  
    }
}

2. 從其他方法返回值獲取,例如gameobject.transform會把transform的ud壓棧並返回並lua,lua通過這個ud進行成員方法的訪問。

static int _g_get_transform(RealStatePtr L)
{
    try {
        ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(L);

        UnityEngine.GameObject gen_to_be_invoked = (UnityEngine.GameObject)translator.FastGetCSObj(L, 1);
        translator.Push(L, gen_to_be_invoked.transform);
    } catch(System.Exception gen_e) {
        return LuaAPI.luaL_error(L, "c# exception:" + gen_e);
    }
    return 1;
}

6.調用成員方法

1. 通過壓棧的ud的元表obj_meta訪問__index方法(xlua.c的obj_indexer),取到方法,非父類方法存在methods。

2. 成員get:同上。

3. 成員set: 通過壓棧的ud的元表訪問__newindex方法(xlua.c的obj_newindexer),取到方法。

4. lua方法調用時會依次把參數壓棧,成員方法(:)調用會先把自身的ud壓棧,再根據ud到objects緩存取到對應的obj實例進行調用。

static int _s_set_widgets(RealStatePtr L)
  {
try {
          ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(L);

          UIHierarchy gen_to_be_invoked = (UIHierarchy)translator.FastGetCSObj(L, 1);//第一個參數,自身ud地址。
          gen_to_be_invoked.widgets = (System.Collections.Generic.List<UIHierarchy.ItemInfo>)translator.GetObject(L, 2, typeof(System.Collections.Generic.List<UIHierarchy.ItemInfo>));
      
      } catch(System.Exception gen_e) {
          return LuaAPI.luaL_error(L, "c# exception:" + gen_e);
      }
      return 0;
  }

一、CS全局表。

LuaEnv的構造方法里會執行init_xlua文本,在這個可執行的文本里生成里CS的全局表,並對CS賦了值

CS = Register["CSHARP_NAMESPACE"]

DoString(init_xlua, "Init");

LuaAPI.xlua_pushasciistring(rawL, CSHARP_NAMESPACE);
if (0 != LuaAPI.xlua_getglobal(rawL, "CS"))
{
    throw new Exception("get CS fail!");
}
LuaAPI.lua_rawset(rawL, LuaIndexes.LUA_REGISTRYINDEX);

init_xlua核心的代碼段如下:

function metatable:__index(key) 
    local fqn = rawget(self,'.fqn')
    fqn = ((fqn and fqn .. '.') or '') .. key

    local obj = import_type(fqn)

    if obj == nil then
        -- It might be an assembly, so we load it too.
        obj = { ['.fqn'] = fqn }
        setmetatable(obj, metatable)
    elseif obj == true then
        return rawget(self, key)
    end

    -- Cache this lookup
    rawset(self, key, obj)
    return obj
end

function metatable:__newindex()
    error('No such type: ' .. rawget(self,'.fqn'), 2)
end

__index的官方說明如下:

__index: 索引 table[key]。 當 table 不是表或是表 table 中不存在 key 這個鍵時,這個事件被觸發。 此時,會讀出 table 相應的元方法。

這個事件的元方法其實可以是一個函數也可以是一張表。 如果它是一個函數,則以 table 和 key 作為參數調用它。 如果它是一張表,最終的結果就是以 key 取索引這張表的結果。 (這個索引過程是走常規的流程,而不是直接索引, 所以這次索引有可能引發另一次元方法。)

這段lua的意思是,如果table[className] 不存在,則執行這個__index。如果className對應的類型存在,加載這個類型到內存(wrap的__Register),否則創建一張空表,當成命名空間處理。

這邊的obj是cls_table,這個表里存着這個類對應wrap的所有靜態方法,通過這個類的wrap文件的__CreateInstance方法可以獲取這個類的ud,而這個ud的元表里封裝了可以獲取類的成員方法、屬性的_index方法。這邊所指的方法都是在wrap里面的。具體后面再說。

__newindex:實際是禁止了對這個表的元表設置操作。

二、命名空間:XLuaLuaDLL

如上的index代碼,local obj = import_type(fqn)。這個obj是nil,那么這邊是創建了兩個空表。即

CS.XLua = {['.fqn'] = fqn}

CS.XLua.LuaDLL = {['.fqn'] = fqn}

三、import_type,加載wrap文件進內存。

1.這邊的import_type實際是StaticLuaCallbacks.ImportType方法。

核心代碼如下,type == null,當做命名空間處理。否則通過translator.GetTypeId加載類。

ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(L);
        string className = LuaAPI.lua_tostring(L, 1);
        Type type = translator.FindType(className);
        if (type != null)
        {
            if (translator.GetTypeId(L, type) >= 0)
            {
                LuaAPI.lua_pushboolean(L, true);
            }
            else
            {
                return LuaAPI.luaL_error(L, "can not load type " + type);
            }
        }
        else
        {
            LuaAPI.lua_pushnil(L);
        }

2.translator.getTypeId:獲取type元表在注冊表中引用Id,如果不存在,則創建元表registry[type.FullName]={},並在registry表中創建新的引用指向該元表並返回type_id。維護了TypeTypeId間的映射表typeMaptypeIdMap

internal int getTypeId(RealStatePtr L, Type type, out bool is_first, LOGLEVEL log_level = LOGLEVEL.WARN)
{
    int type_id;
    is_first = false;
    if (!typeIdMap.TryGetValue(type, out type_id)) // no reference
    {
        is_first = true;
        Type alias_type = null;
        aliasCfg.TryGetValue(type, out alias_type);
        LuaAPI.luaL_getmetatable(L, alias_type == null ? type.FullName : alias_type.FullName);

        if (LuaAPI.lua_isnil(L, -1)) //no meta yet, try to use reflection meta
        {
            LuaAPI.lua_pop(L, 1);

            if (TryDelayWrapLoader(L, alias_type == null ? type : alias_type))
            {
                LuaAPI.luaL_getmetatable(L, alias_type == null ? type.FullName : alias_type.FullName);
            }
        }

        //循環依賴,自身依賴自己的class,比如有個自身類型的靜態readonly對象。
        if (typeIdMap.TryGetValue(type, out type_id))
        {
            LuaAPI.lua_pop(L, 1);
        }
        else
        {
            LuaAPI.lua_pushvalue(L, -1);
            type_id = LuaAPI.luaL_ref(L, LuaIndexes.LUA_REGISTRYINDEX);//register["type_id"] = meta
            LuaAPI.lua_pushnumber(L, type_id);
            LuaAPI.xlua_rawseti(L, -2, 1);//meta["1"] = typeId
            LuaAPI.lua_pop(L, 1);

            if (type.IsValueType())
            {
                typeMap.Add(type_id, type);
            }

            typeIdMap.Add(type, type_id);
        }
    }
    return type_id;
}

3. TryDelayWrapLoader

delayWrap在XLua_Gen_Initer_Register__.Init賦值,Init方法在ObjectTranslator的s_gen_reg_dumb_obj參數生成里調用(即構造函數前就初始化好了)。他實際是Type到TypeWrap.__Register的映射。

下面的loader(L);實際調用的是對應類的wrap文件的__Register方法。

public bool TryDelayWrapLoader(RealStatePtr L, Type type)
        {
            if (loaded_types.ContainsKey(type)) return true;
            loaded_types.Add(type, true);//避免重復加載

            LuaAPI.luaL_newmetatable(L, type.FullName); //先建一個metatable,因為加載過程可能會需要用到
            LuaAPI.lua_pop(L, 1);

            Action<RealStatePtr> loader;
            int top = LuaAPI.lua_gettop(L);
            if (delayWrap.TryGetValue(type, out loader))
            {
                delayWrap.Remove(type);
                loader(L);
            }   
            return true;
        }

四、類的核心加載方法XXXWrap.__Register

這邊不貼代碼了,核心方法都在Utils類里。

public static void __Register(RealStatePtr L)
{
    ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(L);
    System.Type type = typeof(UIHierarchy);

    Utils.BeginObjectRegister(type, L, translator, 0, 2, 3, 3);
    Utils.RegisterFunc(L, Utils.METHOD_IDX, "SetWidgets", _m_SetWidgets);
    Utils.RegisterFunc(L, Utils.GETTER_IDX, "widgets", _g_get_widgets);       
    Utils.RegisterFunc(L, Utils.SETTER_IDX, "widgets", _s_set_widgets);       
    Utils.EndObjectRegister(type, L, translator, null, null,
    null, null, null);

    Utils.BeginClassRegister(type, L, __CreateInstance, 1, 0, 0);
    Utils.RegisterFunc(L, Utils.CLS_IDX, "IsNull", _m_IsNull_xlua_st_);
    Utils.EndClassRegister(type, L, translator);
}

1.BeginObjectRegister:生成類的成員元表、成員方法、GetSet的存放表。

obj_meta[ud]=1

obj_meta["__gc"]=StaticLuaCallbacks.LuaGC

obj_meta["__tostring"]=StaticLuaCallbacks.ToString

方法執行結束:obj_meta, methon, get, set(-4, -3, -2, -1)

2.Utils.RegisterFunc:注冊方法,這個比較簡單,就是做一次賦值。

method_table[name] = func,這邊包括method、set、get等方法設置。

這邊其實把lua調用的方法都封裝成LuaCSFunction委托,再轉成指針賦值給c,c在把這個函數指針封裝成閉包供lua調用。

public static void RegisterFunc(RealStatePtr L, int idx, string name, LuaCSFunction func)
{
   idx = abs_idx(LuaAPI.lua_gettop(L), idx);
   LuaAPI.xlua_pushasciistring(L, name);
   LuaAPI.lua_pushstdcallcfunction(L, func);
   LuaAPI.lua_rawset(L, idx);
}

public static void lua_pushstdcallcfunction(IntPtr L, lua_CSFunction function, int n = 0)//[-0, +1, m]
{
    IntPtr fn = Marshal.GetFunctionPointerForDelegate(function);//轉指針
    xlua_push_csharp_function(L, fn, n);
}

xlua.c接口

static int csharp_function_wrap(lua_State *L) {
   lua_CFunction fn = (lua_CFunction)lua_tocfunction(L, lua_upvalueindex(1));
    int ret = fn(L);    
    return ret;
}

LUA_API void xlua_push_csharp_function(lua_State* L, lua_CFunction fn, int n)
{ 
    lua_pushcfunction(L, fn);
    lua_pushboolean(L, 0);
    lua_pushcclosure(L, csharp_function_wrap, 2 + (n > 0 ? n : 0));
}

3.EndObjectRegister:設置成員元表的__index__newindex方法。

方法開始堆棧:obj_meta,methon,get,set

棧中內容:obj_meta,method,get,set,--index,method,get,csIndexer(array),baseType(父類),register[luaindex],arrayIndexer(nil)

LuaAPI.gen_obj_indexer(L); //設置__index方法

棧中內容:obj_meta,method,get,set,--index, closure(xlua.c 的obj_indexer)

這個閉包如下,提供了根據key在methon,get、basetype中查找方法的實現,很重要。method、get並沒有存在obj_meta,而是存在obj_indexer閉包的上值里。

它會依次查找methods、getters、arrayindexer(數組)、base(基類)。

 

register[luaindex][ud] = obj_indexer

obj_meta[__index]=obj_indexer

棧中內容:obj_meta,method,get,set

棧中內容:obj_meta,method,get,set,__newindex,set,csNewIndexer(nil),baseType(nil),register[LuaNewIndexs],arrayNewIndexer(nil)

LuaAPI.gen_obj_newindexer(L); //設置__newindex方法

棧中內容:obj_meta,method,get,set,__newindex,closure(xlua.x obj_newindexer)

register[LuaNewIndexs][ud] = obj_newindexer

meta[__newindex] = obj_newindexer

方法執行結束:空棧

//upvalue --- [1]: methods, [2]:getters, [3]:csindexer, [4]:base, [5]:indexfuncs, [6]:arrayindexer, [7]:baseindex
//param   --- [1]: obj, [2]: key
LUA_API int obj_indexer(lua_State *L) {    
   if (!lua_isnil(L, lua_upvalueindex(1))) {
      lua_pushvalue(L, 2);
      lua_gettable(L, lua_upvalueindex(1));
      if (!lua_isnil(L, -1)) {//has method
         return 1;
      }
      lua_pop(L, 1);
   }
   
   if (!lua_isnil(L, lua_upvalueindex(2))) {
      lua_pushvalue(L, 2);
      lua_gettable(L, lua_upvalueindex(2));//get直接調用了,所以lua里get不當做方法處理
      if (!lua_isnil(L, -1)) {//has getter
         lua_pushvalue(L, 1);
         lua_call(L, 1, 1);
         return 1;
      }
      lua_pop(L, 1);
   }
   
   
   if (!lua_isnil(L, lua_upvalueindex(6)) && lua_type(L, 2) == LUA_TNUMBER) {
      lua_pushvalue(L, lua_upvalueindex(6));
      lua_pushvalue(L, 1);
      lua_pushvalue(L, 2);
      lua_call(L, 2, 1);
      return 1;
   }
   
   if (!lua_isnil(L, lua_upvalueindex(3))) {
      lua_pushvalue(L, lua_upvalueindex(3));
      lua_pushvalue(L, 1);
      lua_pushvalue(L, 2);
      lua_call(L, 2, 2);
      if (lua_toboolean(L, -2)) {
         return 1;
      }
      lua_pop(L, 2);
   }
   
   if (!lua_isnil(L, lua_upvalueindex(4))) {
      lua_pushvalue(L, lua_upvalueindex(4));
      while(!lua_isnil(L, -1)) {
         lua_pushvalue(L, -1);
         lua_gettable(L, lua_upvalueindex(5));
         if (!lua_isnil(L, -1)) // found
         {
            lua_replace(L, lua_upvalueindex(7)); //baseindex = indexfuncs[base]
            lua_pop(L, 1);
            break;
         }
         lua_pop(L, 1);
         lua_getfield(L, -1, "BaseType");
         lua_remove(L, -2);
      }
      lua_pushnil(L);
      lua_replace(L, lua_upvalueindex(4));//base = nil
   }
   
   if (!lua_isnil(L, lua_upvalueindex(7))) {
      lua_settop(L, 2);
      lua_pushvalue(L, lua_upvalueindex(7));
      lua_insert(L, 1);
      lua_call(L, 2, 1);
      return 1;
   } else {
      return 0;
   }
}

到這邊成員meta的內容填充就結束了。

meta = //LuaAPI.luaL_getmetatable(L, type.FullName) = registry[type.FullName]
{
        ["__index"] = obj_indexer//(見xlua.c)
        ["__newindex"] = obj_newindexer//(見xlua.c)
        [UserData] = 1
        ["__tostring"] = StaticLuaCallbacks.ToString
        [1] = typeId
}

4.BeginClassRegister:生成類的靜態(static)元表、靜態方法、GetSet的存放表。

方法結束 棧:cls_table ,meta_table, get, set(-4, -3, -2, -1)

cls_table[UnderlyingSystemType] = userdata

cls_meta["__call"] = creator(wrap 的 __CreateInstance,該方法會把ud壓棧。

setmetatable(cls_table, cls_meta)

static int __CreateInstance(RealStatePtr L)
{      
    try {
       ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(L);
       if(LuaAPI.lua_gettop(L) == 1)
       {
            UIHierarchy gen_ret = new UIHierarchy();
            translator.Push(L, gen_ret);
                 
            return 1;
       }
    }         
}

5.SetCSTable:生成路徑表。

最終實現的是:XLua.LuaDLL.Lua = cls_table

CS[ud] = cls_table

6.EndClassRegister:設置類的元表的__index__newindex方法

方法開始:cls_table, cls_meta,get,set

:cls_table, cls_meta,get,set,__index,get, cls_table, base(父類), reister["LuaClassIndexs"]

LuaAPI.gen_cls_indexer(L);//設置__index方法

:cls_table, cls_meta,get,set,__index,closure(cls_indexer)

register[LuaClassIndexs][ud] = closure(cls_indexer)

meta[__index] = closure(cls_indexer)

:cls_table,cls_meta,get,set

:cls_table, cls_meta,get,set,__newindex,set, base(父類), reister["LuaClassNewIndexs"]

LuaAPI.gen_cls_newindexer(L); //設置__newindex方法

:cls_table, cls_meta,get,set,__newindex,closure(cls_indexer)

register[LuaClassNewIndexs][ud] = closure(cls_newindexer)

meta[__newindex] = closure(cls_newindexer)

方法結束:空

7.用到的幾個lua表:

1.CLS_IDX對應的cls_table:保存了所有靜態方法的引用。

1.CLS_META_IDX對應的cls_getter:保存了所有get方法的引用,通過cls_meta的__index訪問。

1.CLS_GETTER_IDX對應的cls_setter:保存了所有靜態set的引用,通過cls_meta的__newindex訪問。

1.CLS_SETTER_IDX對應的cls_meta:cls_table的元表。

1.CLS_IDX對應的cls_table:保存了所有靜態方法的引用。

1.CLS_IDX對應的cls_table:保存了所有靜態方法的引用。

五、translator.Push獲取typeuserdata,如果沒有,則創建,保留userdata在棧頂。

1. addObject:將object添加到對象池中,創建object-index的映射關系,通過index可以獲取到c#對象,而通過c#對象可以獲取唯一的index,用於獲取對象的ud,從而訪問對象的從成員方法。

public void Push(RealStatePtr L, object o)
        {
            if (o == null)
            {
                LuaAPI.lua_pushnil(L);
                return;
            }

            int index = -1;
            Type type = o.GetType();
            bool is_enum = type.GetTypeInfo().IsEnum;
            bool is_valuetype = type.GetTypeInfo().IsValueType;
            bool needcache = !is_valuetype || is_enum;

            if (needcache && (is_enum ? enumMap.TryGetValue(o, out index) : reverseMap.TryGetValue(o, out index)))
            {
                //已緩存,通過唯一的typeId取到ud並壓棧
                if (LuaAPI.xlua_tryget_cachedud(L, index, cacheRef) == 1)
                {
                    return;
                }
            }

            bool is_first;
            int type_id = getTypeId(L, type, out is_first);

            if (is_first && needcache && (is_enum ? enumMap.TryGetValue(o, out index) : reverseMap.TryGetValue(o, out index))) 
            {
                if (LuaAPI.xlua_tryget_cachedud(L, index, cacheRef) == 1)
                {
                    return;
                }
            }

            index = addObject(o, is_valuetype, is_enum);
            LuaAPI.xlua_pushcsobj(L, index, type_id, needcache, cacheRef);
        }

2. xlua_pushcsobj:創建一個新的userdata,內容是對象在objects的下標,通過這個下標可以在c#取到對應的對象。

cacheud:cache [index] = ud

setmetatable(ud, meta) 設置元表

也就是說通過translator.Push(L, type)可以獲取對象的userdata,這個userdata的元表obj_meta里封裝了對該類的所有成員方法、get、set屬性的訪問方法,我們可以通過該元表的__index、__newindex調用所有wrap內的成員方法、get、set方法。

static void cacheud(lua_State *L, int key, int cache_ref) {
   lua_rawgeti(L, LUA_REGISTRYINDEX, cache_ref);
   lua_pushvalue(L, -2);
   lua_rawseti(L, -2, key);
   lua_pop(L, 1);
}


LUA_API void xlua_pushcsobj(lua_State *L, int key, int meta_ref, int need_cache, int cache_ref) {
   int* pointer = (int*)lua_newuserdata(L, sizeof(int));
   *pointer = key;
   
   if (need_cache) cacheud(L, key, cache_ref);

    lua_rawgeti(L, LUA_REGISTRYINDEX, meta_ref);

   lua_setmetatable(L, -2);
}

六、總結用到的容器,以及各個meta的內容。

c

register[typeId] = meta //typeId是唯一的

register[type.FullName] = {} 元表是meta,通過LuaAPI.luaL_getmetatable(L, type.FullName);獲取到元表

func_meta = {"__index" = StaticLuaCallbacks.MetaFuncIndex}

下面這個四個表是用於獲取父類的相對應__index方法的。

translator.Push(L, type == null ? base_type : type.BaseType());實際傳到xlua的obj_indexer的是父類的ud,在通過register["LuaIndexs"][ud]獲取父類的obj_indexer閉包。

 

register["LuaIndexs"] = {__index = func_meta}//LuaEnv初始化

register["LuaNewIndexs"] = {__index = func_meta}//LuaEnv初始化

register["LuaClassIndexs"] = {__index = func_meta}//LuaEnv初始化

register["LuaClassNewIndexs"] = {__index = func_meta}//LuaEnv初始化

register["LuaIndexs"][ud] = obj_indexer(這是個閉包,它的上值包括了這個類的method、set、baseType,實際是__index方法,提供對象查找方法)

register["LuaNewIndexs"][ud] = obj_newindexer

register[LuaClassIndexs][ud] = cls_indexer

register[LuaClassNewIndexs][ud] = cls_newindexer

register["LuaIndexs"] = {}//元表的__index指向StaticLuaCallbacks.MetaFuncIndex 其他幾個類似,luaenv初始化

cache = register[cacheRef]

cache [index] = ud (ud = index,元表是typeId對應的meta,即LuaAPI.luaL_getmetatable(L, type.FullName))

xlua_csharp_namespace = register[xlua_csharp_namespace],在LuaEnv 賦值了 register[xlua_csharp_namespace] = CS,即xlua_csharp_namespace = CS

CS [path][type.Name] = cls_table //CS.XLua.LuaDLL.Lua = cls_table

CS [ud] = cls_table


c#

typeMap[type_id] = type

typeIdMap[type] = type_id

objects[index] = obj //可以試試Type

reverseMap[obj] = index

enumMap[obj] = index

obj_meta = //LuaAPI.luaL_getmetatable(L, type.FullName) = registry[type.FullName]
{
        ["__index"] = obj_indexer//(見xlua.c)
        ["__newindex"] = obj_newindexer//(見xlua.c)
        [UserData] = 1
        ["__tostring"] = StaticLuaCallbacks.ToString
        [1] = typeId
}

userdata = index//每個實例都會有一個ud,但是meta是一樣的。ud只是用與區別不同的實例,便於在objects中取得對應的實例對象。
{
        ["__index"] = obj_meta   
}

cls_meta = //這個是新建的表,是新建的cls_table的元表,用於查找靜態的Get方法(包括從基類base查找,靜態設置接口)
{
        ["__call"] = __CreateInstance//(每個類的wrap文件),該方法會把類的ud壓棧,而通過這個ud,我們可以訪問到類的成員方法。
        ["__index"] = cls_indexer//(見xlua.c)
        ["__newindex"] = cls_newindexer//(見xlua.c)
}

cls_table = //每個類都會有一個cls_table,可能會有多個ud.這里保存了所有的靜態方法映射。
{
        staticFunc = func
        ["__index"] = cls_meta
}

七、靜態方法、成員方法的wrap區別。

如下圖所示,成員方法需要調用以下方法取得實例:

UIHierarchy gen_to_be_invoked = (UIHierarchy)translator.FastGetCSObj(L, 1);

也就是它的upvalue第一個參數就是對象的實例,對應lua的調用就是冒號調用:UIHierarchy:SetWidgets,而靜態方法因為不需要傳本身,所以對應lua就是點號調用。

lua點號和冒號區別:https://www.jianshu.com/p/56961e4bd693

 

這邊的index是對象的ud的內存地址。

internal object FastGetCSObj(RealStatePtr L,int index)
{
     return getCsObj(L, index, LuaAPI.xlua_tocsobj_fast(L,index));
}

靜態方法:直接查詢cls_table表,一次查詢。

成員方法:查詢ud-查詢ud的元表obj_meta-調用obj_meta的_index查詢obj_method表。三次查表+一次__index調用。

總結:盡量封裝靜態方法給lua使用,性能計較好。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM