LuaSDK 基础理解(一)

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类似,可以类推。

    原文作者:沧州宁少
    原文地址: https://www.jianshu.com/p/e6f24231d1ff
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞