首页 > 代码库 > CCLuaObjcBridge调Objective-C方法传索引数组报invalid key to 'next'错调试

CCLuaObjcBridge调Objective-C方法传索引数组报invalid key to 'next'错调试

CCLuaObjcBridge是cocos2d-x系列引擎与Objective-C进行交互的“桥梁”,老廖的quick-cocos2d-x在其framework进行了简单了封装,封装到了luaoc类中,大体能够看成:

luaoc.callStaticMethod = CCLuaObjcBridge.callStaticMethod
函数原型例如以下:

--[[
调用Objective-C中的静态方法
@param string className 类名
@param string methodName 方法名
@param table args 參数
]]
function luaoc.callStaticMethod(className, methodName, args)
end

假定有下面Objective-C静态方法:

#import <Foundation/Foundation.h>

@interface SdkApi : NSObject

+ (void)init:(NSDictionary*)dict;

@end
当尝试用下面參数调用时,会报“invalid key to ‘next‘“错:

luaoc.callStaticMethod("SdkApi", "init", {"platform", true})
非常奇怪的错误,一步一步调试,进到CCLuaObjcBridge.mm,定位到lua_next行代码:

        if (hasArguments)
        {
            NSMutableDictionary *dict = [NSMutableDictionary dictionary];
            lua_pushnil(L);
            while (lua_next(L, -2))
            {
                NSString *key = [NSString stringWithCString:lua_tostring(L, -2) encoding:NSUTF8StringEncoding];
                
                switch (lua_type(L, -1))
                {
                    case LUA_TNUMBER:
                        [dict setObject:[NSNumber numberWithFloat:lua_tonumber(L, -1)] forKey:key];
                        break;
                        
                    case LUA_TBOOLEAN:
                        [dict setObject:[NSNumber numberWithBool:lua_toboolean(L, -1)] forKey:key];
                        break;
                        
                    case LUA_TSTRING:
                        [dict setObject:[NSString stringWithCString:lua_tostring(L, -1) encoding:NSUTF8StringEncoding]
                                 forKey:key];
                        break;
                        
                    case LUA_TFUNCTION:
                        int functionId = retainLuaFunction(L, -1, NULL);
                        [dict setObject:[NSNumber numberWithInt:functionId] forKey:key];
                        break;
                }
                
                lua_pop(L, 1);
            }
            
            [invocation setArgument:&dict atIndex:2];
            [invocation invoke];
        }
        else
        {
            [invocation invoke];
        }
粗略阅读了一下代码,没有发现什么异常,于是去查官方文档,最终有了发现:
int lua_next (lua_State *L, int index);

Pops a key from the stack, and pushes a key–value pair from the table at the given index (the "next" pair after the given key). If there are no more elements in the table, then lua_next returns 0 (and pushes nothing).

A typical traversal looks like this:

     /* table is in the stack at index ‘t‘ */
     lua_pushnil(L);  /* first key */
     while (lua_next(L, t) != 0) {
       /* uses ‘key‘ (at index -2) and ‘value‘ (at index -1) */
       printf("%s - %s\n",
              lua_typename(L, lua_type(L, -2)),
              lua_typename(L, lua_type(L, -1)));
       /* removes ‘value‘; keeps ‘key‘ for next iteration */
       lua_pop(L, 1);
     }

While traversing a table, do not call lua_tolstring directly on a key, unless you know that the key is actually a string. Recall that lua_tolstring may change the value at the given index; this confuses the next call to lua_next.

See function next for the caveats of modifying the table during its traversal. 
大体意思是说在遍历table时,除非你知道key是string类型,否则不要直接对key进行lua_tolstring操作,这是由于lua_tolstring操作可能改动指定index处的值,从而使下一次调用lua_next混淆。

简言之就是:lua_tolstring可能会破坏table的原有结构,所以不要在遍历的时候对key进行lua_tolstring操作。

而lua_tostring终于调用的也是lua_tolstring,所以问题便出在这了。

要避免此错误,不传索引数组便可解决,如:

luaoc.callStaticMethod("SdkApi", "init", {platform = "platform", debug = true})

CCLuaObjcBridge调Objective-C方法传索引数组报invalid key to &#39;next&#39;错调试