LuaView SDK第二版設計外掛化理解(二)
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端在棧中獲取相應引數的個數
相關文章
- 外掛化設計二
- “去掉那個框框”外掛--第二版
- Activity外掛化原理第二種方案:Hook IActivityManagerHook
- 外掛化設計三
- Activity的外掛化(二)
- SDK 開發使用 VirtualAPK 實現外掛化APK
- 談談 MyBatis 的外掛化設計MyBatis
- Chrome外掛英雄榜(第二期)Chrome
- Android外掛化原理解析——概要Android
- Android Studio Plugin 外掛開發教程(二) —— 外掛SDK中的常用物件介紹AndroidPlugin物件
- 理解 Babel 外掛Babel
- 二、外掛
- Python 核心程式設計 第二版 中文版(PDF格式)Python程式設計
- 第二屆熵密杯-廣外女生青春版熵
- Android 外掛化原理解析(1):概要Android
- 第二期:JQ外掛編寫入門(2)
- EJB設計模式(第二版)之EJB Command (轉)設計模式
- Qt設計模式(第二版):誰最需要本書?QT設計模式
- [外掛擴充套件]留言版外掛套件
- Android外掛化原理(一)Activity外掛化Android
- 騰訊地圖SDK Flutter外掛實現地圖Flutter
- 無外掛Vim程式設計技巧程式設計
- 設計模式第二講--策略模式設計模式
- 第二章 程式設計之道程式設計
- 第二週程式設計練習程式設計
- Flutter 外掛開發:以微信SDK為例Flutter
- 好書推薦:深入理解Linux核心(第二版)Linux
- Android 外掛化原理解析(7):廣播的管理Android
- WordPress外掛開發例項教程 - 版權外掛
- 第二篇:從 GPU 的角度理解平行計算GPU
- [外掛擴充套件]微信二維碼展示外掛套件
- 影像處理第二篇之波段運算(軟體外掛篇)
- MFC Window程式設計(第二版)精華濃縮筆記(三) (轉)程式設計筆記
- Learn Forge 系列:Forge 雲端自動化設計服務 - 外掛部分
- electron 外掛系統設計記錄
- 前端程式設計提高之旅(一)----外掛前端程式設計
- 程式設計師十誡:第二誡:不可把程式神聖化程式設計師
- 第二小節 go 語言設計Go