iOS中调用Lua2

585 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第4天,点击查看活动详情

6 方法的传递

Lua中只能接受C定义的方法传入,并且方法的声明必须符合lua_CFunction函数指针的定义,即:

int functionName (lua_State *state);

那么,传入方法则需要先定义一个C语言声明的方法,如:

int printHelloWorld (lua_State *state)
{
    NSLog(@"Hello World!");
    return 0;
}

方法里面简单地进行了一下信息打印,其中方法的返回值是一个整数,表明了该方法需要返回多少个值到Lua中(后续章节会进行返回值的相关演示),现在不需要返回值则为0。然后,再通过lua_pushcfunction方法将方法传入:

lua_pushcfunction(self.state, printHelloWorld);
lua_setglobal(self.state, "funcVal");

操作完成后,在Lua中就可以直接调用了:

funcVal();

如果定义的方法是允许接受参数的,那么可以从state参数里面获取传入的参数。拿上面的例子,例如方法接收一个名字的字符串参数,函数的代码则可以修改为:

int printHelloWorld (lua_State *state)
{
    if (lua_gettop(state) > 0)
    {
        //表示有参数
        const char *name = lua_tostring(state, 1);
        NSLog(@"Hello %s!", name);
    }

    return 0;
}

然后在Lua中则可以这样调用:

funcVal ("vimfung");

如果定义的方法不是直接打印字符串,而是组合了字符串给Lua返回,那么定义的方法里面则需要配合’lua_pushXXXX’系列方法来进行返回值传递。需要注意的是:方法中return的数量要与push到栈中的值要一致,否则可能出现异常。 那么,上面定义的函数可以做如下修改:

int printHelloWorld (lua_State *state)
{
    if (lua_gettop(state) > 0)
    {
        //表示有参数
        const char *name = lua_tostring(state, 1);

        //入栈返回值
        NSString *retVal = [NSString stringWithFormat:@"Hello %s!", name];
        lua_pushstring(state, retVal.UTF8String);

        return 1;
    }

    return 0;
}

然后在Lua中则可以这样调用:

local retVal = funcVal("vimfung");
print(retVal);

7 数组和字典的传递

在Lua中,数组(Array)和字典(Dictionary)都由一个Table类型所表示(在Lua看来数组其实也属于一种字典,只是它的key是有序并且为整数)。如:

-- 定义数组
local arrayVal = {1,2,3,4,5,6};
-- 定义字典
local dictVal = {a=1, b=3, c=4, d=5};

上面的例子分别用了不带key的声明和带key的声明两种方式来创建Table类型。其中不带key的声明方式,解析器会默认为其创建一个key,该key是从1开始,由小到大进行分配,其等效于:

local arrayVal = {1=1, 2=2, 3=3, 4=4, 5=5, 6=6};

当然,两种方式是可以混合使用,如:

local tbl = {1, 2, a=1, b=2, 3};

Table属于比较复杂的数据结构,因此提供操作它的C Api也比较复杂,下面将根据数组和字典分别讲述它们的传递方式。

7.1 数组传递

首先,需要将一个Table类型入栈,这样才能对其进行进一步的操作。由于没有pushtable这样的方法,但是可以使用lua_newtable来创建一个Table对象,并且该对象会自动放入栈顶位置。如:

lua_newtable(self.state);

然后对要传递的数组进行遍历,并通过lua_rawseti方法将元素值设置到Table中。如:

NSArray *array = @[@1, @2, @3, @4, @5, @6];
 [array enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {

    NSInteger value = [obj integerValue];
    lua_pushinteger(self.state, value);
    lua_rawseti(self.state, -2, idx + 1);

}];

lua_getglobal(self.state, "arrayVal");

通过上面的代码就可以把一个数组传递给arrayVal变量。值得注意的是:lua_rawseti方法表示要栈顶的元素设置给指定的Table对象的指定索引。其中的第二个参数是指Table对象在栈中的位置,第三个参数是表示在Table中的索引,一般索引是从1开始算起,因此上面代码中的idx需要加1。经过这样的操作后,栈顶的元素会被移除。如下图所示:

image.png

7.2 字典传递

字典的传递同样需要先入栈一个Table:

lua_newtable(self.state);

然后对要传递的字典进行遍历,并通过lua_setfield方法将元素设置到Table中。如:

[dict enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {

    NSInteger value = [obj integerValue];
    lua_pushinteger(self.state, value);
    lua_setfield(self.state, -2, key.UTF8String);

}];

lua_setglobal(self.state, "dictVal");

lua_setfieldlua_rawseti功能类型,都是把一个元素放入Table中,只是一个用于指定整数索引,一个是指定字符串索引。通过上面的方式就可以把字典传递给Lua了。