LuaView 第一版SDK的理解
- LVBaseView
通过把原生类实例构建成userdata的方式映射到Lua 中。userdata->object指向类的内存。通过为userdata设置原表的方式关联类实例的function。
每个类的baseMemberFunctions返回一个结构体指针,即结构体数组,返回当前userdata元表中的所有func。
下面对具体的代码做解析
static int hidden(lua_State *L) { // Lua虚拟栈的栈底始终返回传入的对象本身的指针。lua_touserdata(L,index)第一个参数为L虚拟机,第二个参数为栈相应的位置。返回对象的内存地址。 LVUserDataInfo * user = (LVUserDataInfo *)lua_touserdata(L, 1); if( user ){ // 从封装的userdata的object指针取出相应的原生类实例 UIView* view = (__bridge UIView *)(user->object); if( view ){ // 如果栈内元素大于2,即调用的为set方法。 // lua_gettop返回栈顶元素的索引,即返回栈内元素的个数。 if ( lua_gettop(L)>=2 ) { // 指定位置元素转换为bool值。 BOOL yes = lua_toboolean(L, 2); view.hidden = yes; //设置返回值的个数。Lua虚拟机会根据返回个数。将栈内指定个数的元素出栈 return 0; } else { // 向栈内推入Bool值到栈顶 lua_pushboolean(L, view.hidden ); return 1; } } } return 0; }
通过上面的例子可以感受到Lua通过栈把Lua的元素传递过来,然后对Lua类型的变量做解析。真实的逻辑处理依赖需要元素去处理。
原生类型转为Lua类型传入。lv_pushNativeObject方法把这一过程进行封装。
void lv_pushNativeObject(lua_State* L, id value){ // 检查栈空间大小 lua_checkstack(L, 4); if( [value isKindOfClass:[NSString class]] ) { NSString* s = value; // 将const char * 压栈 lua_pushstring(L, s.UTF8String); return; } else if( [value isKindOfClass:[NSDictionary class]] ) { NSDictionary* dictionary = value; // 创建一个table并压栈 lua_newtable(L); for (NSString *key in dictionary) { NSString* value = dictionary[key]; lua_checkstack(L, 4); // 将key 压栈。 lua_pushstring(L, key.UTF8String); // value对象是id类型。需要递归去处理 lv_pushNativeObject(L,value); // 此时栈内元素为 [str,id,table] // lua_settable(L,-3)为设置对应table的key为-2的value为栈顶元素。设置后key,value出栈。 lua_settable(L, -3); // 此时栈内元素 [table] table = {str=id} } return; } else if( [value isKindOfClass:[NSArray class]] ) { NSArray* array = value; lua_newtable(L); for (int i=0; i<array.count; i++) { id value = array[i]; // 推入number到栈中 lua_pushnumber(L, i+1); // 同理递归转换为lua可识别对象 lv_pushNativeObject(L,value); // 同上。[id,number,table] lua_settable(L, -3); // [table] table = {1= id} } return; } else if( [value isKindOfClass:[NSNumber class]] ) { // 同理lua_pushnumber(L,num),lua_pushboolean将对应元素位置压栈 static Class boolClass = nil;; if ( boolClass ==nil ) { boolClass = [@(YES) class]; } NSNumber* number = value; if( [value class] == boolClass) { // 是否是bool类型 lua_pushboolean(L, number.boolValue); return; } else { lua_pushnumber(L, number.doubleValue); return; } } else if( value==nil || value == [NSNull null] ) { // lua_pushnil(L) 将nil元素压栈 lua_pushnil(L); return; } else if( [value isKindOfClass:[LVBlock class] ] ) { // 1、将LVBlock中的retainKey 作为lightuserdata的方式压栈; 2.从注册表获取retainKey对应的func,然后将对应的Func压栈。 LVBlock* block = (LVBlock*)value; [block pushFunctionToStack]; return; } else { // 将OC对象通过box包装一下压栈,压到栈内的为userdata,稍后讲解这个Api lv_pushNativeObjectWithBox(L, value); return; } }
void lv_pushNativeObjectWithBox(lua_State * L, id nativeObject ){
// 实例化一个Box对象通过nativeObject
LVNativeObjBox* nativeObjBox = [[LVNativeObjBox alloc] init:L nativeObject:nativeObject];
nativeObjBox.openAllMethod = YES;// 所有api都开放
// 创建一个userdata对象关联nativeObject 并压栈
NEW_USERDATA(userData, NativeObject);
// ARC不处理当前对象了,把引用权利交给Lua GC
userData->object = CFBridgingRetain(nativeObjBox);
// 关联native的userData为当前的userdata。
nativeObjBox.lv_userData = userData;
// 获取元表并压栈
luaL_getmetatable(L, META_TABLE_NativeObject);
// 设置对应位置的userData的元表为栈顶的表。
lua_setmetatable(L, -2);
}
LVBaseView中有一个注册方法。
+(int) lvClassDefine:(lua_State *)L globalName:(NSString*) globalName{
// 通过cls和lvNewView这个Func合并成一个closure 然后关联到全局表的Name中 具体参考下面的Api
[LVUtil reg:L clas:self cfunc:lvNewView globalName:globalName defaultName:@"View"];
// 创建metable 并压栈
lv_createClassMetaTable(L, META_TABLE_UIView);
// 将结构体数组。循环以结构体的name为key。结构体的func为value写入metableTable
luaL_openlib(L, NULL, [LVBaseView baseMemberFunctions], 0);
// 同理如上。创建META_TABLE_LuaView原表
lv_createClassMetaTable(L, META_TABLE_LuaView);
// 将LVBaseView所有内容写入元表
luaL_openlib(L, NULL, [LVBaseView baseMemberFunctions], 0);
// luaViewMemberFunctions结构体数组内容写入元表
luaL_openlib(L, NULL, luaViewMemberFunctions, 0);
return 1;
}
LVUtil reg方法如下
+(void) reg:(lua_State*)L clas:(id) c cfunc:(lua_CFunction) cfunc globalName:(NSString*)globalName defaultName:(NSString*) defaultName{
if( defaultName || globalName ) {
// 检查栈空间大小
lua_checkstack(L, 12);
// 通过类获取类名
NSString* className = NSStringFromClass(c);
// str 压栈
lua_pushstring(L, className.UTF8String);
// 传入1则会根据-1位置的元素及func生成闭包名
lua_pushcclosure(L, cfunc, 1);
// 设置global或者default到Lua虚拟机的全局表。当在Lua中调用Collection的时候会根据key找到传入的cFunc地址。然后()会调用这个Func生成native 对象。
// lua_setglobal(L,name) 会将栈顶元素出栈,并设置到全局表对应的Key。
lua_setglobal(L, globalName ? globalName.UTF8String : defaultName.UTF8String );
}
}
luaL_openlib()方法的定义如下
LUALIB_API void luaI_openlib (lua_State *L, const char *libname,
const luaL_Reg *l, int nup) {
if (libname) {
int size = 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 */
}
// 一般libName传入null
for (; l->name; l++) {
int i;
for (i=0; i<nup; i++) /* copy upvalues to the top */
lua_pushvalue(L, -nup);
// 向栈内压入closure
lua_pushcclosure(L, l->func, nup);
// 设置-2位置的table。此时为元表。设置name为key,栈顶元素为value,然后栈顶元素出栈。
lua_setfield(L, -(nup+2), l->name);
}
lua_pop(L, nup); /* remove upvalues */
}
其他的LuaSDK中的View和LVBaseView类似,可以类推。