正文
Lua环境的维护需要一个叫lua_State的结构体来支持,其贯穿了整个执行过程。因此,要使用Lua则需要先初始化一个lua_State结构体。修改ViewController的代码如下:
上面说到数据“栈”的概念,C Api中提供了很多操作栈的功能接口,通常可以分为四大类:入栈操作、查询操作、取值操作和其他操作。
6.1 入栈操作
表示要将本地的某个类型的值放到数据栈中,然后提供给Lua层来获取和操作。该类操作接口有如下定义:
从上面的接口方法定义可以看出来,不同的Lua类型对应着不通的入栈接口,包括了整型(Integer)、布尔类型(Boolean)、浮点数(Number)、字符串(String)、闭包(Closure)、用户自定义数据(Userdata)、空类型(Nil)以及线程(Thread)。需要注意的是,C Api没有提供直接入栈Table类型的接口(估计是该数据类型无法与本地结构进行对应),如果需要入栈一个Table类型,可以使用lua_createtable方法来入栈一个Table,调用该方法会在栈顶放入一个Table的引用。
可见,假如我们需要在原生代码中给Lua的一个全局变量a赋一个整型值,那么可以如下面代码的做法:
其中的lua_setglobal方法为设置全局变量的值,该方法会把数据栈顶的元素放入该方法第二个参数所指定的变量名对应的变量中,同时移除栈顶元素。如图:
lua_setglobal示意图
6.2 查询操作
之前说到栈中的每个元素都可以为任意类型,那么,对于如何判断元素的类型就可以通过该类方法来实现。该类方法的定义如下:
同样查询操作也是提供了不同的方法来检测不同的类型。其中第二个参数表示要检测类型的元素处于栈中的哪个位置。
关于栈中位置在lua中有两种形式表示,第一种是正数表示法,1表示栈底元素(即最先入栈的元素),然后越往上的元素,索引值越大。另外一种是负数表示法,-1表示栈顶元素(即最后入栈的元素),然后越往下的元素,索引值越小。如图所示:
栈索引两种表示方法示意图
lua_isXXX系列方主要是判断栈中数据是否能够被转换为对应数据类型时使用,如lua_isstring方法则是判断栈中某个元素是否能够被转换为string类型,所以当栈中数据为number类型时,其返回值也为true。
如果要进行非转换的强类型判断,可以使用lua_type方法来获取栈中元素的类型,然后根据类型来获取值。如判断栈顶元素的类型:
6.3 取值操作
栈中的所有元素的获取都是通过该类方法来实现,通常该类方法跟在查询类方法后,当知道某个数据类型后,则调用对应数据类型的取值方法来获取元素。其方法定义如下:
取值操作的接口也相当简单,分别传入lua_State对象和栈索引即可。如果在调用时指定的类型跟栈中类型不同也不会有什么问题,接口会因为类型不正确而返回0或者NULL。
要注意的是该系列接口跟lua_isXXX系列接口一样,会对原始的类型进行转换输出,因此在做一些跟类型相关的操作时,最好时先判断类型再根据类型调用该方法取值,否则会导致一些意想不到的异常,如下面例子:
上面例子中的原意是要输出下面的内容:
但是实际上却是这样:
这是由于使用了lua_tostring把栈中的targetVar的值改变了导致的,所以类似这样的操作一定要谨慎。
6.4 其他操作
使用上述3部分的操作可以满足与栈中数据进行交互的大多数情况。如果需要更加灵活地对栈进行操作,例如拷贝栈中某个元素,交互栈中元素位置等等的操作可以使用下面所定义的接口:
其中lua_gettop为获取栈顶位置,也即是栈中元素的个数,其实这个方法在处理原生方法的传入参数时很有用,可以确认传入参数的个数。有时候也可以用它来输出各个状态下的栈元素变化,来确认自己在操作栈时是否存在问题。
lua_settop方法用于设置栈顶位置,如果新栈顶高于之前的栈顶则会push一些nil的元素来填充;如果新栈顶低于之前的栈顶则会丢弃新栈顶之上的所有元素。如图所示:
lua_settop示意图
lua_pushvalue方法表示将栈中某个元素的副本压入栈顶。之前的栈元素不会发生变动。如图所示: