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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,039评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,223评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,916评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,009评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,030评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,011评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,934评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,754评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,202评论 1 309
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,433评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,590评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,321评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,917评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,568评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,738评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,583评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,482评论 2 352

推荐阅读更多精彩内容