LuaView SDK第二版設計插件化理解(二)
第一篇提到了MILExporter類,及它的相關的Api,下面看一下它里面的具體實現(xiàn)。
- (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);
}
-
這個文件的一個核心函數(shù)。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)); // 將相應數(shù)量的元素 出棧 + 存儲上面壓棧的值 + 相應的閉包壓棧 + lua_pushcclosure(L, l->func, (nup+extraCount)); // 找到 -(nup+2) 這個位置上面的表,設置l->l_mn為key,棧頂元素為value 寫入表。 lua_setfield(L, -(nup+2), l->l_mn); } // 棧中移除相應數(shù)量的元素 lua_pop(L, nup); /* remove upvalues */ }下面來看下非常核心的Lua與native的通訊部分,劉旭對原來的通訊方式進行完全的改變。這是LuaViewSDK優(yōu)化最核心的部分。
上面一直提到我們自己構建了結構體對象。我們映射到Lua的只是這個結構體,映射到lua的全部類方法和對象方法都可以通過這個結構體的MMLua_Method 表示。Lua 去掉用userdata方法,原本通過各個單獨的方法調用,現(xiàn)在改為全部都路由到一個C的Api里面,然后在這個Api里面進行消息的分發(fā)。下面看下具體的結構體。
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 */
// 初始化函數(shù)的描述
struct mm_lua_objc_method constructor; /* Object-C constructor method */
}mm_lua_objc_class;
下面以 LUA_EXPORT_VIEW_PROPERTY(image, "lua_setImage:", "lua_image", MMGraphicButton) 為例??聪戮唧w這個宏定義為我們做了什么。連續(xù)點擊進去可以發(fā)現(xiàn)這個宏最終會我們生成mm_lua_objc_method 這個結構體的一個實例。{image,NULL,MMGraphicButton,YES,"lua_setImage","lua_image",mm_lua_router_method};
- 此時我們再去查看發(fā)現(xiàn)所有的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數(shù)組中。具體的閉包調用的時候在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棧中參數(shù)的個數(shù)獲取是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,參數(shù) 4. 執(zhí)行invocation的invoke的操作 5. 通過invocation的returnType 確定返回值的個數(shù) **/ 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]; // 確定方法前面中參數(shù)的個數(shù) NSUInteger argsCount = [sig numberOfArguments] - 2; // Lua堆棧中個數(shù) 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]; // 后續(xù)是參數(shù)的設置 NSMutableArray *retainArray = [NSMutableArray arrayWithCapacity:argsCount]; // retain invocation中的target和參數(shù) [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) { // 通過方法前面獲取指定位置參數(shù)的描述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; // 參數(shù)類型不支持 } } } NSCAssert(NO, @"An error occurred about the parameter type!"); return 2; // 獲取參數(shù)類型失敗 } /** 獲取oc數(shù)據(jù)類型 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中。對應了所有的參數(shù)的類型對應的字符串
#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) {
// 通過方法簽名拿到返回參數(shù)的字符串描述
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;
// 傳入?yún)?shù)的地址。方法內部會根據(jù)傳入的指針會修改相應內存的值
[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; // 參數(shù)類型不支持
}
}
}
NSCAssert(NO, @"An error occurred about the parameter type!");
return 0;
}
上面對應每一種返回的參數(shù)類型 做了switch。在invocation中獲取相應的參數(shù)。然后壓入lua棧中。普通數(shù)據(jù)類型,轉成Lua可以接收的類型壓棧,Lua不能直接接收的id類型。并沒有繼續(xù)采用SDK內部的包裝一個LVBox的方式。而是進行了自己的創(chuàng)新,把每一種具體的類型都展開。當然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 關聯(lián)真實的data
mm_lua_pushnativedata(L, value);
} else if( value && [value isKindOfClass:[NSDate class]] ) {
// 直接使用LVDate。關聯(lián)真實的日期。然后將生成的userdata壓棧
mm_lua_pushnativedate(L, value);
} else if ([value conformsToProtocol:@protocol(MILEntityClassProtocol)]){
NSCAssert([[value class] respondsToSelector:@selector(pluginOfLua)], @"The @sel(pluginOfLua) must be responds!");
// 最經典的~ 。 滿足相應協(xié)議的,直接通過協(xié)議拿到相應的插件,插件通過setUp根據(jù)宿主類的相關信息生成userdata 然后壓棧。
mm_lua_pushEntity(L, (id<MILEntityClassProtocol>)value);
} else {
NSCAssert(NO, @"An error occurred about the parameter type!");
}
}
返回值為Lua端在棧中獲取相應參數(shù)的個數(shù)