一起养成写作习惯!这是我参与「掘金日新计划 · 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把值弹出栈的。