iOS中调用Lua3

132 阅读3分钟

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

8 自定义数据传递

Lua中一个比较强大的地方是它可以将任意的类型(包括类对象)进行传递。特别是在提供原生处理方法时,需要用到一些特定的数据类型作为参数时,Lua就可以帮我们实现这一块的传递。

要想传递自定义的数据则必须要使用Lua提供的Userdata类型。该类型有两种引用方式,一种是强引用Userdata,由Lua的GC来负责该类型变量的生命周期。另外一种是弱引用Userdata,又称Light Userdata,该类型不被GC所管理,其生命周期由原生层来决定。下面来看一下两种方式是如何实现的。

首先我们来定义一个OC的User类:

@interface User : NSObject

@property (nonatomic, copy) NSString *name;

@end

@implementation User

@end

然后,利用lua_newuserdata方法来创建一个强引用Userdata,并创建一个User对象赋值给新建的Userdata。如:

void *instanceRef = lua_newuserdata(self.state, sizeof(User *));
instanceRef = (__bridge_retained void *)[[User alloc] init];
lua_setglobal(self.state, "userdataVal");

通过上面的代码就可以把User类实例封装成Userdata再传递给Lua。如果你要传递的对象并不需要Lua来管理生命周期,那么就可以创建一个弱引用的Userdata,如:

User *user = [[User alloc] init];
lua_pushlightuserdata(self.state, (__bridge void *)(user));
lua_setglobal(self.state, "userdataVal");

下面来看一个比较实际的例子,假设有一个提供给Lua调用的原生接口printUser,该接口会打印传入进来的用户信息,代码如下:

static int printUser (lua_State *state)
{
    if (lua_gettop(state) > 0)
    {
        //表示有参数传入
        User *user = (__bridge User *)(lua_topointer(state, 1));
        NSLog(@"user.name = %@", user.name);
    }

    return 0;
}

该方法通过lua_topointer方法来获取了一个Userdata数据类型并转换为User类实例对象然后打印其名称。接下来将其导出给Lua:

lua_pushcfunction(self.state, printUser);
lua_setglobal(self.state, "printUser");

然后生成一个User对象,并调用该方法传入该用户对象。如:

//创建User对象
User *user = [[User alloc] init];
user.name = @"vimfung";

lua_getglobal(self.state, "printUser");
//传入User参数
lua_pushlightuserdata(self.state, (__bridge void *)(user));
lua_pcall(self.state, 1, 0, 0);

上面的代码就是使用C Api来调用Lua的方法(下面的章节会详细讲述这块内容),通过OC代码创建了一个User对象并将其作为了参数传给了Lua的printUser方法。最终的输出信息如下:

user.name = vimfung

从OC到Lua的所有类型的转换和交互基本上都涉及到了,下面章节将会详细描述从Lua到OC上的一些交互和数据交换。

From Lua to OC

1 获取数值

通过lua_tonumber方法可以获取某个数值变量的值。如:

lua_getglobal(self.state, "aa");
double value = lua_tonumber(self.state, -1);
NSLog(@"aa = %f", value);
lua_pop(self.state, 1);

上述代码中的lua_getglobal方法是用于获取全局变量的值,调用它会把一个值放入栈顶。然后再通过lua_tonumber把栈顶的值读取出来并打印。需要注意的是通过lua_getglobal获取得到的值,在栈中是不会自动清除,因此,在用完某个变量时记得把它从栈中清除掉,代码中是通过lua_pop把值弹出栈的。