LuaView SDK第二版設計外掛化理解(二)

weixin_33890499發表於2018-05-29

LuaView SDK第二版設計外掛化理解(二)

第一篇提到了MILExporter類,及它的相關的Api,下面看一下它裡面的具體實現。

- (void)reg:(lua_State *)L clazz:(const char *)clazzName constructor:(const char *)constructorName cfunc:(lua_CFunction)cfunc name:(const char *)luaName 註冊key到Lua虛擬機器的全域性表。 傳入了className,constructorName。以className為key,具體的閉包為value進行寫入。具體程式碼如下。
        + (void)reg:(lua_State *)L clazz:(const char *)clazzName constructor:(const char *)constructorName cfunc:(lua_CFunction)cfunc name:(const char *)luaName
      
      {
      
          NSAssert(mm_CharPointIsNotNULL(clazzName), @"The class must not be nil!");
      
          NSAssert(mm_CharPointIsNotNULL(luaName), @"The lua name of class must not be nil!");
      
          lua_checkstack(L, 12);
         
          // 把native類名壓棧
          lua_pushstring(L, clazzName);
          // 是否為屬性壓棧。這個暫時沒用到
          lua_pushboolean(L, NO); // 不是屬性
          // 把初始化方法名稱壓棧。如果沒有的話則走預設的
          lua_pushstring(L, mm_CharPointIsNotNULL(constructorName) ? constructorName : "init");
          // 設定closure的upValue 出棧相應的元素,閉包壓棧
          lua_pushcclosure(L, cfunc, 3);
           // 設定閉包到全域性表
          lua_setglobal(L, luaName);
      
      }
  • 這個檔案的一個核心函式。void mm_lua_openlib (lua_State *L, const char *libname, const struct mm_lua_objc_method *l, int nup) { 註冊例項方法到元表和註冊類方法到類對應的表。

    void mm_lua_openlib (lua_State *L, const char *libname,
                         const struct mm_lua_objc_method *l, int nup) {
      // 如果libname存在。則相當於註冊類方法。
        if (libname) {
            int size = mm_libsize(l);
            /* check whether lib already exists */
            luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 1);
            lua_getfield(L, -1, libname);  /* get _LOADED[libname] */
            if (!lua_istable(L, -1)) {  /* not found? */
                lua_pop(L, 1);  /* remove previous result */
                /* try global variable (and create one if it does not exist) */
                if (luaL_findtable(L, LUA_GLOBALSINDEX, libname, size) != NULL)
                    luaL_error(L, "name conflict for module " LUA_QS, libname);
                lua_pushvalue(L, -1);
                lua_setfield(L, -3, libname);  /* _LOADED[libname] = new table */
            }
            lua_remove(L, -2);  /* remove _LOADED table */
            lua_insert(L, -(nup+1));  /* move library table to below upvalues */
        }
        // 上面的過程相當於在LUA_GLOBALSINDEX 表中獲取libname對應的表
        for (; l->l_mn; l++) {
            NSCAssert(mm_CharPointIsNotNULL(l->clz), @"The class name must not be nil!");
            NSCAssert(l->func!=NULL, @"The C function must not be NULL!");
            int extraCount = 0;
            // 類名壓棧
            lua_pushstring(L, l->clz); // class
            // bool 屬性壓棧
            lua_pushboolean(L, l->isProperty);
            if (l->isProperty) {
                NSCAssert(mm_CharPointIsNotNULL(l->setter_n), @"The method name must not be nil!");       // 為屬性則setter_n和getter_n 字串壓棧
                lua_pushstring(L, l->setter_n); // setter
                NSCAssert(mm_CharPointIsNotNULL(l->getter_n), @"The method name must not be nil!");
                lua_pushstring(L, l->getter_n); // getter
                extraCount = 4;
            } else {
                NSCAssert(mm_CharPointIsNotNULL(l->mn), @"The method name must not be nil!");      
                //否則則 selector名稱壓棧
                lua_pushstring(L, l->mn); // selector
                extraCount = 3;
            }
            int i;
            for (i=0; i<nup; i++)  /* copy upvalues to the top */
                lua_pushvalue(L, -(nup+extraCount));
            // 將相應數量的元素 出棧 +  儲存上面壓棧的值 +  相應的閉包壓棧 +
            lua_pushcclosure(L, l->func, (nup+extraCount));
            // 找到 -(nup+2) 這個位置上面的表,設定l->l_mn為key,棧頂元素為value 寫入表。
            lua_setfield(L, -(nup+2), l->l_mn);
        }
        // 棧中移除相應數量的元素
        lua_pop(L, nup);  /* remove upvalues */
    }
    

    下面來看下非常核心的Lua與native的通訊部分,劉旭對原來的通訊方式進行完全的改變。這是LuaViewSDK優化最核心的部分。

  • 上面一直提到我們自己構建了結構體物件。我們對映到Lua的只是這個結構體,對映到lua的全部類方法和物件方法都可以通過這個結構體的MMLua_Method 表示。Lua 去掉用userdata方法,原本通過各個單獨的方法呼叫,現在改為全部都路由到一個C的Api裡面,然後在這個Api裡面進行訊息的分發。下面看下具體的結構體。

    struct mm_lua_objc_method ;
    typedef struct mm_lua_objc_method *MMLua_Method;
    struct mm_lua_objc_class;
    typedef struct mm_lua_objc_class *MMLua_Class;
    
    typedef struct mm_lua_objc_method{
        const char *l_mn;      /* Object-C method name in lua*/
        const char *mn;       /* Object-C method name */
        const char *clz;      /* Object-C class name */
        BOOL isProperty;      /* It's YES if property method*/
        const char *setter_n;   /* Object-C getter method name*/
        const char *getter_n;   /* Object-C setter method name */
        lua_CFunction func;     // 元表對應的func。註冊進去的都是func。
    }mm_lua_objc_method;
    
    typedef struct mm_lua_objc_class{
        const  char *pkg;  // package name 
        const  char *clz;  // 類名
        const  char *l_clz;  ///* Object-C class name in lua */
        const char *l_type; /* its type of Object-C class in lua  */
        BOOL isRoot; /* is root function,it should be YES if no base class. */
        const char *supreClz; /* base Object-C class */
        BOOL hasConstructor; /* it should be NO if static class. */
        MMLua_Method methods; /* Object-C method */
        MMLua_Method clz_methods; /* Object-C class method */
        // 初始化函式的描述
        struct mm_lua_objc_method constructor; /* Object-C constructor method */
        
    }mm_lua_objc_class;

下面以 LUA_EXPORT_VIEW_PROPERTY(image, "lua_setImage:", "lua_image", MMGraphicButton) 為例。看下具體這個巨集定義為我們做了什麼。連續點選進去可以發現這個巨集最終會我們生成mm_lua_objc_method 這個結構體的一個例項。{image,NULL,MMGraphicButton,YES,"lua_setImage","lua_image",mm_lua_router_method};

  • 此時我們再去檢視發現所有的Lua去呼叫物件方法實際都會路由到我們的mm_lua_router_method中,然後通過這個方法再去生成相應的Invocation 進行invoke呼叫。我們來具體看下mm_lua_router_method具體做了什麼。
    int mm_lua_router_method (lua_State *L) {
       NSCAssert(L, @"The lua state must not be null!");
       // class 首先獲取Class
       Class clazz = mm_lua_resolveClass(L);
       // 同理在closure的上值中獲取是否為Property
       BOOL isProperty = mm_lua_resolveIsProperty(L);
       // 獲取SEL。 如果非屬性則直接在上值中獲取Sel名稱轉為SEL
       SEL selector = isProperty ? mm_lua_resolveGetterOrSetter(L) : mm_lua_resolveSelector(L);
       // 方法的呼叫。
       return mm_lua_call_objc_method(L, selector, clazz);
    }
    
    Class mm_lua_resolveClass (lua_State *L) {
       NSCAssert(L, @"The lua state must not be null!");
       NSCAssert(lua_isstring(L, lua_upvalueindex(1)), @"The first upvalue must be a string of class name!");
       // 通過偽索引 去獲取Closure中的上值。
       NSString *clazzString = [NSString stringWithUTF8String:lua_tostring(L, lua_upvalueindex(1))];
       NSCAssert(clazzString && clazzString.length > 0, @"The class name must not be nil!");
       // 將上值儲存的string轉成Class。上面的openLib方法涉及到了上值的儲存。會儲存到生成的Closure的upValue陣列中。具體的閉包呼叫的時候在Lua虛擬機器的ci的func拿到func,此時為TValue物件,通過TValue找到Closure。
       return NSClassFromString(clazzString);
    }
    // 在上值中獲取是否為Property.
    BOOL mm_lua_resolveIsProperty (lua_State *L) {
       NSCAssert(L, @"The lua state must not be null!");
       NSCAssert(lua_isboolean(L, lua_upvalueindex(2)), @"The first upvalue must be a string of selector name!");
       return lua_toboolean(L, lua_upvalueindex(2));
    }
    //  同理通過Lua棧中引數的個數獲取是get還是set方法。然後通過上值將String轉成Selector
    SEL mm_lua_resolveGetterOrSetter (lua_State *L) {
       NSCAssert(L, @"The lua state must not be null!");
       int selIdx = [MILExporter isLuaCallGetter:L] ? 4 : 3; // setter's index is 3, getter's index is 4
       return mm_lua_resolveSelectorAtIndex(L, selIdx);
    }
    
  • 上面提到了mm_lua_call_objc_method 下面來看下到底做了什麼。
     int mm_lua_call_objc_method (lua_State *L, SEL selector, Class<MILEntityClassProtocol> clazz) {
         NSCAssert(L, @"The lua state must not be null!");
         // 在棧底獲取Userdata物件並進行型別判斷。返回userdata->object物件
         id targetObj = [MILExporter targetOnLuaCall:L class:clazz];
         // 重置LuaCore
         mm_lua_resetLuaCore(L, clazz);
         // 進行真實的方法的呼叫。
         return mm_lua_call_objc(L, targetObj, selector, NO);
     }
     
     /*
             1. 通過SEL獲取方法簽名
             2. 通過方法簽名生成NSInvocation
             3. 設定Invocation的SEL,target,引數
             4. 執行invocation的invoke的操作
             5. 通過invocation的returnType 確定返回值的個數
     **/
     int mm_lua_call_objc (lua_State *L, id target, SEL selector, BOOL isclass) {
         NSCAssert(L, @"The lua state must not be null!");
         NSCAssert(target, @"The target must not be nil!");
         // 獲取方法前面通過SEL
         NSMethodSignature *sig = [target methodSignatureForSelector:selector];
         // 確定方法前面中引數的個數
         NSUInteger argsCount = [sig numberOfArguments] - 2;
         // Lua堆疊中個數
         int l_argsCount = [MILExporter numOfArgsOnLuaCallIn:L];
     //    NSCAssert(argsCount==l_argsCount, @"The number of parameters does not match!");
         // 通過方法簽名生成NSInvocation 物件
         NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sig];
         // 設定target
         [invocation setTarget:target];
         // 設定SEL
         [invocation setSelector:selector];
         // 後續是引數的設定
         NSMutableArray *retainArray = [NSMutableArray arrayWithCapacity:argsCount];
         // retain invocation中的target和引數
         [invocation retainArguments];
         for (int i = 2; i <= argsCount + 1; i++) {
             int argIdx = i;
             int stackIdx = i;
             MMTypeConvertorErr success = mm_setInvocationArgByLua(invocation, argIdx, L, stackIdx, retainArray, !isclass);
             switch (success) {
                 case MMTypeConvertorUndef:
                     lua_settop(L, 0);
                     lua_pushstring(L, "Undefined parameter type");
                     return 1;
                 case MMTypeConvertorTypeErr:
                      lua_settop(L, 0);
                     lua_pushstring(L, "An error occurred about the parameter type");
                     return 1;
                 default:
                     break;
             }
         }
         // invocation的呼叫。
         [invocation invoke];
         return mm_pushInvocationReturnToLua(invocation, L);
     }
    
     // 下面具體看一下setArgument: atIndex:的過程
     
     int mm_setInvocationArgByLua(NSInvocation *invocation, int index, lua_State *L, int stackID, NSMutableArray *retainArray, BOOL hasTarger4Block) {
         // 通過方法前面獲取指定位置引數的描述const char* 
         const char* type = [invocation.methodSignature getArgumentTypeAtIndex:index];
         if (type){
             switch (mm_typeOfObjc(type)) {
                 case MM_OBJCType_BOOL: {
                     // Lua 棧相應位置拿到值
                     BOOL value = lua_toboolean(L, stackID);
                     // 設定到invocation相應的位置
                     [invocation setArgument:&value atIndex:index];
                     return 0;
                 }
                 case MM_OBJCType_class: {
                     if (lua_isstring(L, stackID)) {
                         Class clazz = NSClassFromString(lv_paramString(L, stackID));
                         [invocation setArgument:&clazz atIndex:index];
                     }
                     return 0;
                 }
                 case MM_OBJCType_block: {
                     if (lua_isfunction(L, stackID)) {
                         MILBlock *block = nil;
                         if (hasTarger4Block) {
                             block = [[MILBlock alloc] initWithTarget:invocation.target luaCore:LV_LUASTATE_VIEW(L) index:stackID];
                         } else {
                             block = [[MILBlock alloc] initWithLuaCore:LV_LUASTATE_VIEW(L) index:stackID];
                         }
                         MILCallback callback = [^(id result, BOOL keepAlive){
                             [block callWithParam:result];
                         } copy];
                         [retainArray addObject:callback];
                         // 設定callback到指定位置
                         [invocation setArgument:&callback atIndex:index];
                     }
                     return 0;
                 }
                 case MM_OBJCType_SEL: {
                     if (lua_isstring(L, stackID)) {
                         SEL selector = NSSelectorFromString(lv_paramString(L, stackID));
                         [invocation setArgument:&selector atIndex:index];
                     }
                     return 0;
                 }
                 case MM_OBJCType_id: {
                     id nativeObject = nil;
                     if (hasTarger4Block) {
                         // lua物件轉native物件
                         nativeObject = mm_lua_tonativeobj(L, stackID, invocation.target);
                     } else {
                         nativeObject = mm_lua_tonativeobj(L, stackID, nil);
                     }
                     [invocation setArgument:&nativeObject atIndex:index];
                     return 0;
                 }
                 case MM_OBJCType_char: {
                     char value = lua_tonumber(L, stackID);
                     [invocation setArgument:&value atIndex:index];
                     return 0;
                 }
                 case MM_OBJCType_uchar: {
                     unsigned char value = lua_tonumber(L, stackID);
                     [invocation setArgument:&value atIndex:index];
                     return 0;
                 }
                 case MM_OBJCType_short: {
                     short value = lua_tonumber(L, stackID);
                     [invocation setArgument:&value atIndex:index];
                     return 0;
                 }
                 case MM_OBJCType_ushort: {
                     unsigned short value = lua_tonumber(L, stackID);
                     [invocation setArgument:&value atIndex:index];
                     return 0;
                 }
                 case MM_OBJCType_int: {
                     int value = lua_tonumber(L, stackID);
                     [invocation setArgument:&value atIndex:index];
                     return 0;
                 }
                 case MM_OBJCType_uint: {
                     unsigned int value = lua_tonumber(L, stackID);
                     [invocation setArgument:&value atIndex:index];
                     return 0;
                 }
                 case MM_OBJCType_long: {
                     long value = lua_tonumber(L, stackID);
                     [invocation setArgument:&value atIndex:index];
                     return 0;
                 }
                 case MM_OBJCType_ulong: {
                     unsigned long value = lua_tonumber(L, stackID);
                     [invocation setArgument:&value atIndex:index];
                     return 0;
                 }
                 case MM_OBJCType_llong: {
                     long long value = lua_tonumber(L, stackID);
                     [invocation setArgument:&value atIndex:index];
                     return 0;
                 }
                 case MM_OBJCType_ullong: {
                     unsigned long long value = lua_tonumber(L, stackID);
                     [invocation setArgument:&value atIndex:index];
                     return 0;
                 }
                 case MM_OBJCType_float: {
                     float value = lua_tonumber(L, stackID);
                     [invocation setArgument:&value atIndex:index];
                     return 0;
                 }
                 case MM_OBJCType_double: {
                     double value = lua_tonumber(L, stackID);
                     [invocation setArgument:&value atIndex:index];
                     return 0;
                 }
                 case MM_OBJCType_char_ptr: {
                     char *value = lua_touserdata(L, stackID);
                     [invocation setArgument:&value atIndex:index];
                     return 0;
                 }
                 case MM_OBJCType_void_ptr: {
                     void *value = lua_touserdata(L, stackID);
                     [invocation setArgument:&value atIndex:index];
                     return 0;
                 }
                 case MM_OBJCType_const_char_ptr: {
                     const char *value = lua_touserdata(L, stackID);
                     [invocation setArgument:&value atIndex:index];
                     return 0;
                 }
                 case MM_OBJCType_rect: {
                     CGRect orig_rect = mm_lua_tocgrect(L, stackID);
                     [invocation setArgument:&orig_rect atIndex:index];
                     return 0;
                 }
                 case MM_OBJCType_size: {
                     CGSize orig_size = mm_lua_tocgsize(L, stackID);
                     [invocation setArgument:&orig_size atIndex:index];
                     return 0;
                 }
                 case MM_OBJCType_point: {
                     CGPoint orig_point = mm_lua_tocgpoint(L, stackID);
                     [invocation setArgument:&orig_point atIndex:index];
                     return 0;
                 }
                 default: {
                     NSInteger value = 0;
                     NSCAssert(value, @"Undefined parameter type!");
                     [invocation setArgument: &value atIndex:index];
                     LVError(@"Undefined parameter type!");
                     return 1; // 引數型別不支援
                 }
             }
         }
         NSCAssert(NO, @"An error occurred about the parameter type!");
         return 2; // 獲取引數型別失敗
     }
     
     /**
      獲取oc資料型別 mm_typeOfObjc
      */
     MM_Objc_Type mm_typeOfObjc(const char *type) {
         switch (type[0]) {
                 // id型別
             case _C_ID: //#define _C_ID       '@'
                 // 則返回Block
                 if (type[1] == _C_UNDEF) {    '?'
                     return MM_OBJCType_block;
                 }
                 return MM_OBJCType_id;
             case _C_CLASS: //#define _C_CLASS    '#'
                 return MM_OBJCType_class;
             case _C_SEL:  //#define _C_SEL      ':'
                 return MM_OBJCType_SEL;
             case _C_CHR:  //#define _C_CHR      'c'
                 return MM_OBJCType_char;
             case _C_UCHR: //#define _C_UCHR     'C'
                 return MM_OBJCType_uchar;
             case _C_SHT:  //#define _C_SHT      's'
                 return MM_OBJCType_short;
             case _C_USHT: //#define _C_USHT     'S'
                 return MM_OBJCType_ushort;
             case _C_INT:  //#define _C_INT      'i'
                 return MM_OBJCType_int;
             case _C_UINT: //#define _C_UINT     'I'
                 return MM_OBJCType_uint;
             case _C_LNG:  //#define _C_LNG      'l'
                 return MM_OBJCType_long;
             case _C_ULNG: //#define _C_ULNG     'L'
                 return MM_OBJCType_ulong;
             case _C_LNG_LNG: //#define _C_LNG_LNG  'q'
                 return MM_OBJCType_llong;
             case _C_ULNG_LNG: //#define _C_ULNG_LNG 'Q'
                 return MM_OBJCType_ullong;
             case _C_FLT: //#define _C_FLT      'f'
                 return MM_OBJCType_float;
             case _C_DBL: //#define _C_DBL      'd'
                 return MM_OBJCType_double;
             case _C_BOOL: //#define _C_BOOL     'B'
                 return MM_OBJCType_BOOL;
             case _C_VOID: //#define _C_VOID     'v'
                 return MM_OBJCType_void;
             case _C_CHARPTR: //#define _C_CHARPTR  '*'
                 return MM_OBJCType_char_ptr;
             case _C_STRUCT_B: { //#define _C_STRUCT_B '{' 結構體
                 // #define mm_strcmp(a, b) (strcmp((a), (b)) == 0)
                 if (mm_strcmp(type, @encode(CGRect))) {
                     return MM_OBJCType_rect;
                 } else if (mm_strcmp(type, @encode(CGSize))) {
                     return MM_OBJCType_size;
                 } else if (mm_strcmp(type, @encode(CGPoint))) {
                     return MM_OBJCType_point;
                 }
                 return MM_OBJCType_struct;
             }
             case _C_PTR: { //#define _C_PTR      '^'
                 if (type[1] == _C_ID) { ? 
                     return MM_OBJCType_id_ptr;
                 }  else if (type[1] == _C_STRUCT_B) {
                     return MM_OBJCType_struct_ptr;
                 }  else if (mm_strcmp(type, @encode(void *))) {
                     return MM_OBJCType_void_ptr;
                 }
                 //TODO: 待支援其他型別
                 return MM_OBJCType_ndef;
             }
             case _C_CONST: { //#define _C_CONST    'r' 常量
                 if (mm_strcmp(type, @encode(const char *))) {
                     return MM_OBJCType_const_char_ptr;
                 }
                 //TODO: 待支援其他型別
                 return MM_OBJCType_ndef;
             }
             default:  //#define _C_UNDEF    '?'
                 return MM_OBJCType_ndef;
         }
     }
    

上面提到的各種type_VOID等 實際對應了一張表在runtime.h中。對應了所有的引數的型別對應的字串

#define _C_ID       '@'
#define _C_CLASS    '#'
#define _C_SEL      ':'
#define _C_CHR      'c'
#define _C_UCHR     'C'
#define _C_SHT      's'
#define _C_USHT     'S'
#define _C_INT      'i'
#define _C_UINT     'I'
#define _C_LNG      'l'
#define _C_ULNG     'L'
#define _C_LNG_LNG  'q'
#define _C_ULNG_LNG 'Q'
#define _C_FLT      'f'
#define _C_DBL      'd'
#define _C_BFLD     'b'
#define _C_BOOL     'B'
#define _C_VOID     'v'
#define _C_UNDEF    '?'
#define _C_PTR      '^'
#define _C_CHARPTR  '*'
#define _C_ATOM     '%'
#define _C_ARY_B    '['
#define _C_ARY_E    ']'
#define _C_UNION_B  '('
#define _C_UNION_E  ')'
#define _C_STRUCT_B '{'
#define _C_STRUCT_E '}'
#define _C_VECTOR   '!'
#define _C_CONST    'r'

下面看下Lua端呼叫以上方法返回部分。也就是呼叫方法後,相應的native物件需要轉為相應的lua支援的物件壓棧。

int mm_pushInvocationReturnToLua(NSInvocation* invocation, lua_State* L) {
    // 通過方法簽名拿到返回引數的字串描述
    const char *type = [invocation.methodSignature methodReturnType];
    if (type){
        // 上面已經說了。具體看上面runtime的一個表去對應
        switch (mm_typeOfObjc(type)) {
            case MM_OBJCType_void:
                return 0;
            case MM_OBJCType_BOOL: {
                BOOL result = 0;
                // 傳入引數的地址。方法內部會根據傳入的指標會修改相應記憶體的值
                [invocation getReturnValue: &result];
                lua_pushboolean(L, result);
                return 1;
            }
            case MM_OBJCType_class: {
                Class clazz = nil;
                [invocation getReturnValue:&clazz];
                if (clazz) {
                    lua_pushstring(L, NSStringFromClass(clazz).UTF8String);
                } else {
                    lua_pushnil(L);
                }
                return 1;
            }
            case MM_OBJCType_SEL: {
                SEL sel = nil;
                [invocation getReturnValue:&sel];
                if (sel) {
                    lua_pushstring(L, NSStringFromSelector(sel).UTF8String);
                } else {
                    lua_pushnil(L);
                }
                return 1;
            }
            case MM_OBJCType_id: {
                void *result = nil;
                [invocation getReturnValue:&result];
                mm_lua_pushnativeobj(L,(__bridge id)result);
                return 1;
            }
            case MM_OBJCType_char: {
                char result = 0;
                [invocation getReturnValue: &result];
                lua_pushnumber(L, result);
                return 1;
            }
            case MM_OBJCType_uchar: {
                unsigned char result = 0;
                [invocation getReturnValue: &result];
                lua_pushnumber(L, result);
                return 1;
            }
            case MM_OBJCType_short: {
                short result = 0;
                [invocation getReturnValue: &result];
                lua_pushnumber(L, result);
                return 1;
            }
            case MM_OBJCType_ushort: {
                unsigned short result = 0;
                [invocation getReturnValue: &result];
                lua_pushnumber(L, result);
                return 1;
            }
            case MM_OBJCType_int: {
                int result = 0;
                [invocation getReturnValue: &result];
                lua_pushnumber(L, result);
                return 1;
            }
            case MM_OBJCType_uint: {
                unsigned int result = 0;
                [invocation getReturnValue: &result];
                lua_pushnumber(L, result);
                return 1;
            }
            case MM_OBJCType_long: {
                long result = 0;
                [invocation getReturnValue: &result];
                lua_pushnumber(L, result);
                return 1;
            }
            case MM_OBJCType_ulong: {
                unsigned long result = 0;
                [invocation getReturnValue: &result];
                lua_pushnumber(L, result);
                return 1;
            }
            case MM_OBJCType_llong: {
                long long result = 0;
                [invocation getReturnValue: &result];
                lua_pushnumber(L, result);
                return 1;
            }
            case MM_OBJCType_ullong: {
                unsigned long long result = 0;
                [invocation getReturnValue: &result];
                lua_pushnumber(L, result);
                return 1;
            }
            case MM_OBJCType_float: {
                float result = 0;
                [invocation getReturnValue: &result];
                lua_pushnumber(L, result);
                return 1;
            }
            case MM_OBJCType_double: {
                double result = 0;
                [invocation getReturnValue: &result];
                lua_pushnumber(L, result);
                return 1;
            }
            case MM_OBJCType_char_ptr: {
                char *result = 0;
                [invocation getReturnValue: &result];
                lua_pushlightuserdata(L, result);
                return 1;
            }
            case MM_OBJCType_void_ptr: {
                void *result = 0;
                [invocation getReturnValue: &result];
                lua_pushlightuserdata(L, result);
                return 1;
            }
            case MM_OBJCType_rect:{
                CGRect rect = CGRectZero;
                [invocation getReturnValue:&rect];
                mm_lua_pushcgrect(L, rect);
                return 1;
            }
            case MM_OBJCType_size:{
                CGSize size = CGSizeZero;
                [invocation getReturnValue:&size];
                mm_lua_pushcgsize(L, size);
                return 1;
            }
            case MM_OBJCType_point:{
                CGPoint point = CGPointZero;
                [invocation getReturnValue:&point];
                mm_lua_pushcgpoint(L, point);
                return 1;
            }
            default: {
                NSInteger value = 0;
                NSCAssert(value, @"Undefined parameter type!");
                [invocation setArgument: &value atIndex:index];
                LVError(@"Undefined parameter type!");
                return 0; // 引數型別不支援
            }
        }
    }
    NSCAssert(NO, @"An error occurred about the parameter type!");
    return 0;
}

上面對應每一種返回的引數型別 做了switch。在invocation中獲取相應的引數。然後壓入lua棧中。普通資料型別,轉成Lua可以接收的型別壓棧,Lua不能直接接收的id型別。並沒有繼續採用SDK內部的包裝一個LVBox的方式。而是進行了自己的創新,把每一種具體的型別都展開。當然userdata的話轉為相應的userdata並壓棧。具體程式碼如下。

void mm_lua_pushnativeobj (lua_State* L, id value) {
    lua_checkstack(L, 4);
    
    if( [value isKindOfClass:[NSString class]] ) {
        NSString* s = value;
        lua_pushstring(L, s.UTF8String);
    } else if( [value isKindOfClass:[NSDictionary class]] ) {
        NSDictionary* dictionary = value;
        lua_newtable(L);
        for (NSString *key in dictionary) {
            NSString* value = dictionary[key];
            lua_checkstack(L, 4);
            lua_pushstring(L, key.UTF8String);
            mm_lua_pushnativeobj(L,value);
            lua_settable(L, -3);
        }
    } else if( [value isKindOfClass:[NSArray class]] ) {
        NSArray* array = value;
        lua_newtable(L);
        for (int i=0; i<array.count; i++) {
            id value = array[i];
            lua_pushnumber(L, i+1);
            mm_lua_pushnativeobj(L,value);
            lua_settable(L, -3);
        }
    } else if( [value isKindOfClass:[NSNumber class]] ) {
        static Class boolClass = nil;;
        if ( boolClass == nil ) {
            boolClass = [@(YES) class];
        }
        NSNumber* number = value;
        if( [value class] == boolClass) {
            //  是否是bool型別
            lua_pushboolean(L, number.boolValue);
        } else {
            lua_pushnumber(L, number.doubleValue);
        }
    }  else if( value == nil || value == [NSNull null] ) {
        lua_pushnil(L);
    }  else if( value && [value isKindOfClass:[UIImage class]] ) {
        // 直接使用LVBitmap。生成Bitmap包裝成userdata壓棧
        mm_lua_pushnativeimage(L, value);
    }  else if( value && [value isKindOfClass:[NSData class]] ) {
        // 直接使用LVData 關聯真實的data
        mm_lua_pushnativedata(L, value);
    }  else if( value && [value isKindOfClass:[NSDate class]] ) {
        // 直接使用LVDate。關聯真實的日期。然後將生成的userdata壓棧
        mm_lua_pushnativedate(L, value);
    } else if ([value conformsToProtocol:@protocol(MILEntityClassProtocol)]){
        NSCAssert([[value class] respondsToSelector:@selector(pluginOfLua)], @"The @sel(pluginOfLua) must be responds!");
        // 最經典的~ 。 滿足相應協議的,直接通過協議拿到相應的外掛,外掛通過setUp根據宿主類的相關資訊生成userdata 然後壓棧。
        mm_lua_pushEntity(L, (id<MILEntityClassProtocol>)value);
    } else {
        NSCAssert(NO, @"An error occurred about the parameter type!");
    }
}

返回值為Lua端在棧中獲取相應引數的個數

相關文章