
名稱 ? ? ? ? ?說明
docLua ? ?相關(guān)的文檔,包括了編譯文檔、接口文檔等
Makefile ? 編譯Lua使用,在這里我們不使用它來進(jìn)行編譯
README ? 關(guān)于Lua的說明文件
src ? ? ? ? ? ? ?Lua的源碼文件
注意:在這里我們只需要src目錄中的源碼文件,先打開src目錄,將Makefile、lua.c、luac.c三個(gè)文件刪除掉,需要說明的是lua.c和luac.c文件是用于編譯生成lua和luac兩個(gè)命令不屬于解析器的功能,如果不刪除可能會(huì)導(dǎo)致XCode無法編譯通過。
ios system函數(shù)不可用:
1.導(dǎo)入頭文件:#include<ftw.h>
2.聲明unlink_cb方法:
int unlink_cb(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf)
{
? ? int rv = remove(fpath);
? ? if (rv)
? ? ? ? perror(fpath);
?? ?????return rv;
}
3.使用nftw:
nftw(luaL_optstring(L, 1, NULL), unlink_cb, 64, FTW_DEPTH|FTW_PHYS)

1.c api
在開始實(shí)現(xiàn)Lua與OC交互之前先來了解兩個(gè)非常重要的概念,一個(gè)是Lua的C Api,Lua解釋器是使用C語言來編寫的,因此它提供了豐富的C語言定義的接口來訪問和操作Lua中的所有元素;
另外一個(gè)就是棧?的概念,在Lua和C進(jìn)行交互數(shù)據(jù)的時(shí)候會(huì)用到了一個(gè)棧的結(jié)構(gòu),棧中的每個(gè)元素都能保存任何類型的Lua值。要獲取Lua中的一個(gè)值時(shí),需要調(diào)用一個(gè)C Api函數(shù),Lua就會(huì)將特定的值壓入棧中,然后再通過相應(yīng)的C Api將值取出來

同樣,要將一個(gè)值傳給Lua時(shí),需要先調(diào)用C Api將這個(gè)值壓入棧,然后再調(diào)用C Api,Lua就會(huì)獲取該值并將其從棧中彈出。 如圖所示:

關(guān)于棧中位置在lua中有兩種形式表示,第一種是正數(shù)表示法,1表示棧底元素(即最先入棧的元素),然后越往上的元素,索引值越大。另外一種是負(fù)數(shù)表示法,-1表示棧頂元素(即最后入棧的元素),然后越往下的元素,索引值越小。如圖所示:

1.關(guān)于棧操作的C Api
C Api中提供了很多操作棧的功能接口,通??梢苑譃樗拇箢悾喝霔2僮?、查詢操作、取值操作和其他操作。
1.1 入棧操作
表示要將本地的某個(gè)類型的值放到數(shù)據(jù)棧中,然后提供給Lua層來獲取和操作。
void lua_pushnil (lua_State *L);
void lua_pushnumber (lua_State *L, lua_Number n);
void lua_pushinteger (lua_State *L, lua_Integer n);
const char *lua_pushlstring (lua_State *L, const char *s, size_t len)?;
const char *lua_pushstring (lua_State *L, const char *s);
const char *lua_pushvfstring (lua_State *L, const char *fmt, va_list argp);
const char *lua_pushfstring (lua_State *L, const char *fmt, ...);
void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n);
void lua_pushboolean (lua_State *L, int b);
void lua_pushlightuserdata (lua_State *L, void *p);
int lua_pushthread (lua_State *L);
從上面的接口方法定義可以看出來,不同的Lua類型對(duì)應(yīng)著不通的入棧接口,包括了整型(Integer)、布爾類型(Boolean)、浮點(diǎn)數(shù)(Number)、字符串(String)、閉包(Closure)、用戶自定義數(shù)據(jù)(Userdata)、空類型(Nil)以及線程(Thread)。需要注意的是,C Api沒有提供直接入棧Table類型的接口(估計(jì)是該數(shù)據(jù)類型無法與本地結(jié)構(gòu)進(jìn)行對(duì)應(yīng)),如果需要入棧一個(gè)Table類型,可以使用lua_createtable方法來入棧一個(gè)Table,調(diào)用該方法會(huì)在棧頂放入一個(gè)Table的引用。
例子:lua_pushinteger (state, 1); ?//把1這個(gè)值放到棧頂
lua_setglobal (state, "a"); ?//把棧頂?shù)脑胤湃朐摲椒ǖ诙€(gè)參數(shù)所指定的變量名對(duì)應(yīng)的lua變量中,同時(shí)移除棧頂元素。如圖:

?1.2查詢操作
之前說到棧中的每個(gè)元素都可以為任意類型,那么,對(duì)于如何判斷元素的類型就可以通過該類方法來實(shí)現(xiàn)。該類方法的定義如下:
int lua_isnil (lua_State *state, int index);
int lua_isboolean (lua_State *state, int index);
int lua_isfunction (lua_State *state, int index);
int lua_istable (lua_State *state, int index);
int lua_islightuserdata (lua_State *state, int index);
int lua_isthread (lua_State *state, int index);
int lua_isnumber (lua_State *L, int idx);
int lua_isinteger (lua_State *L, int idx);
int lua_iscfunction (lua_State *L, int idx);
int lua_isstring (lua_State *L, int idx);
int lua_isuserdata (lua_State *L, int idx)
注意第二個(gè)參數(shù)index,可以有兩種形式;第一種是n位于棧頂,第二種是-1位于棧頂;如下圖:

lua_isXXX系列方主要是判斷棧中數(shù)據(jù)是否能夠被轉(zhuǎn)換為對(duì)應(yīng)數(shù)據(jù)類型時(shí)使用,如lua_isstring方法則是判斷棧中某個(gè)元素是否能夠被轉(zhuǎn)換為string類型,所以當(dāng)棧中數(shù)據(jù)為number類型時(shí),其返回值也為true。
如果要進(jìn)行非轉(zhuǎn)換的強(qiáng)類型判斷,可以使用lua_type方法來獲取棧中元素的類型,然后根據(jù)類型來獲取值。如判斷棧頂元素的類型:
switch(lua_type(state, -1)){
? ? case LUA_TNUMBER:
? ? ? ? break;
? ? case LUA_TSTRING:
? ? ? ? break;
? ? ?。。。。。。。
? ? ? default:
? ? ? ? break;
}
1.3 取值操作
當(dāng)知道某個(gè)數(shù)據(jù)類型后,則調(diào)用對(duì)應(yīng)數(shù)據(jù)類型的取值方法來獲取元素。其方法定義如下:
int lua_toboolean (lua_State *L, int idx);
const char *lua_tolstring (lua_State *L, int idx, size_t *len);
lua_CFunction lua_tocfunction (lua_State *L, int idx);
void *lua_touserdata (lua_State *L, int idx);
lua_State *lua_tothread (lua_State *L, int idx);
const void *lua_topointer (lua_State *L, int idx);
lua_Integer lua_tointegerx (lua_State *L, int idx, int *pisnum);
lua_Number lua_tonumberx (lua_State *L, int idx, int *pisnum);
要注意的是該系列接口跟lua_isXXX系列接口一樣,會(huì)對(duì)原始的類型進(jìn)行轉(zhuǎn)換輸出,因此在做一些跟類型相關(guān)的操作時(shí),最好時(shí)先判斷類型再根據(jù)類型調(diào)用該方法取值
1.4 其他操作
int lua_gettop (lua_State *L);
void lua_settop (lua_State *L, int idx);
void lua_pushvalue (lua_State *L, int idx);
void lua_remove(lua_State *L, int idx);
void lua_insert(lua_State *L, int idx);
void lua_replace(lua_State *L, int idx);
void lua_pop(lua_State *L, int n);
lua_gettop:獲取棧頂位置,也即是棧中元素的個(gè)數(shù),其實(shí)這個(gè)方法在處理原生方法的傳入?yún)?shù)時(shí)很有用,可以確認(rèn)傳入?yún)?shù)的個(gè)數(shù)。有時(shí)候也可以用它來輸出各個(gè)狀態(tài)下的棧元素變化,來確認(rèn)自己在操作棧時(shí)是否存在問題。
lua_settop:用于設(shè)置棧頂位置,如果新棧頂高于之前的棧頂則會(huì)push一些nil的元素來填充;如果新棧頂?shù)陀谥暗臈m攧t會(huì)丟棄新棧頂之上的所有元素。如圖所示:

lua_pushvalue:表示將棧中某個(gè)元素的副本壓入棧頂。之前的棧元素不會(huì)發(fā)生變動(dòng)。如圖所示:

lua_remove方法用于移除指定索引上的元素,然后再該元素之上的所有元素會(huì)下移填補(bǔ)空缺(即元素的索引會(huì)發(fā)生變更)。如圖所示:

lua_insert會(huì)將指定索引位置之上的所有元素上移來開辟一個(gè)新的位置。然后將棧頂元素插入到該位置。如圖所示:

lua_replace方法會(huì)先彈出棧頂元素,然后將該元素覆蓋到指定索引位置上(棧就減少了少了一個(gè)元素)。如圖所示:

lua_pop方法會(huì)從棧頂彈出指定數(shù)量的元素。如圖所示:

2.?From OC to Lua
2.1 空值傳遞
使用lua_pushnil方法可以將任意一個(gè)Lua變量置空。如:
lua_pushnil(); ?//推到棧上
lua_setglobal(self.state, "val"); //棧上到c空間
2.2 數(shù)值的傳遞
使用lua_pushinteger或者lua_pushnumber方法來將OC中的數(shù)值類型傳遞到Lua中指定的某個(gè)變量。如:
//傳遞整型值
lua_pushinteger(self.state, 1024);
lua_setglobal(self.state, "intVal");
//傳遞浮點(diǎn)型
lua_pushnumber(self.state, 80.08);
lua_setglobal(self.state, "numVal");
2.3 布爾值的傳遞
使用lua_pushboolean方法來實(shí)現(xiàn),如:
lua_pushboolean(self.state, YES);
lua_setglobal(self.state, "boolVal");
2.4 字符串的傳遞
使用lua_pushstring方法可以傳遞字符串給Lua,要注意的是該方法接收的是一個(gè)c描述的字符串(即 char*)。如:
lua_pushstring(self.state, @"Hello World".UTF8String);
lua_setglobal(self.state, "stringVal");
2.5 二進(jìn)制數(shù)組的傳遞
二進(jìn)制數(shù)組在Lua中其實(shí)與字符串的存儲(chǔ)方式相同,但是OC中不能直接使用lua_pushstring來進(jìn)行二進(jìn)制數(shù)組的傳遞,可以使用lua_pushlstring方法來傳遞。如:
char bytes[13] = {0xf1, 0xaa, 0x12, 0x56, 0x00, 0xb2, 0x43, '\0', '\0', 0x00, 0x90, 0x65, 0x73};
lua_pushlstring(self.state, bytes, 13);
lua_setglobal(self.state, "bytesVal");
2.6 方法的傳遞
Lua中只能接受C定義的方法傳入,并且方法的聲明必須符合lua_CFunction函數(shù)指針的定義,即:
int printHelloWorld (lua_State *state){
? ? NSLog(@"Hello World!");
? ? return 0;
}
lua_pushcfunction(self.state, printHelloWorld);
lua_setglobal(self.state, "funcVal");
操作完成后,在Lua中就可以直接調(diào)用了:
funcVal();
如果oc的方法是允許接受參數(shù)的,那么可以從state參數(shù)里面獲取傳入的參數(shù)。拿上面的例子,例如方法接收一個(gè)名字的字符串參數(shù),函數(shù)的代碼則可以修改為:
int printHelloWorld (lua_State*state){
? ? if(lua_gettop(state)>0){
? ? ? ? const char*name =lua_tostring(state,1); //取值操作會(huì)刪除棧頂元素
? ? ? ? const char*name2 =lua_tostring(state,2);
? ? ? ? NSLog(@"Hello--%s--%s",name,name2); //
? ? }
? ? return 0;
}
lua中調(diào)用:funcVal("aaa","bbb")
輸出:Hello--aaa--bbb
如果定義的方法不是直接打印字符串,而是組合了字符串給Lua返回,那么定義的方法里面則需要配合’lua_pushXXXX’系列方法來進(jìn)行返回值傳遞。需要注意的是:方法中return的數(shù)量要與push到棧中的值要一致,否則可能出現(xiàn)異常。那么,上面定義的函數(shù)可以做如下修改:
int printHelloWorld (lua_State*state){
? ? if(lua_gettop(state)>0){
? ? ? ? constchar*name =lua_tostring(state,1);
? ? ? ? NSString*retVal = [NSStringstringWithFormat:@"Hollo %s",name];
? ? ? ? lua_pushstring(state, retVal.UTF8String);
? ? ? ? return 1; //直接返回,因?yàn)橹挥幸粋€(gè)return,那么也只能push一次
? ? }
? ? return 0;
}
然后在Lua中則可以這樣調(diào)用:
local retVal = funcVal("vimfung");
print(retVal);
輸出:Hollo vimfung
2.7 數(shù)組和字典的傳遞
在Lua中,數(shù)組(Array)和字典(Dictionary)都由一個(gè)Table類型所表示(在Lua看來數(shù)組其實(shí)也屬于一種字典,只是它的key是有序并且為整數(shù))。如:
-- 定義數(shù)組 local arrayVal = {1,2,3,4,5,6};
-- 定義字典 local dictVal = {a=1, b=3, c=4, d=5};
上面的例子分別用了不帶key的聲明和帶key的聲明兩種方式來創(chuàng)建Table類型。其中不帶key的聲明方式,解析器會(huì)默認(rèn)為其創(chuàng)建一個(gè)key,該key是從1開始,由小到大進(jìn)行分配,其等效于:
local arrayVal = {1=1, 2=2, 3=3, 4=4, 5=5, 6=6};
當(dāng)然,兩種方式是可以混合使用,如:
local tbl = {1, 2, a=1, b=2, 3};
Table屬于比較復(fù)雜的數(shù)據(jù)結(jié)構(gòu),因此提供操作它的C Api也比較復(fù)雜,下面將根據(jù)數(shù)組和字典分別講述它們的傳遞方式。
2.7.1 數(shù)組傳遞
首先,需要將一個(gè)Table類型入棧,這樣才能對(duì)其進(jìn)行進(jìn)一步的操作。由于沒有pushtable這樣的方法,但是可以使用lua_newtable來創(chuàng)建一個(gè)Table對(duì)象,并且該對(duì)象會(huì)自動(dòng)放入棧頂位置。如:
lua_newtable(self.state); //創(chuàng)建一個(gè)Table對(duì)象并放入棧頂
然后對(duì)要傳遞的數(shù)組進(jìn)行遍歷,并通過lua_rawseti方法將元素值設(shè)置到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); //將當(dāng)前棧頂元素放入Table中,第二個(gè)參數(shù)代表Table在棧中的位置,第三個(gè)參數(shù)代表放入Table的位置
}];
lua_setglobal(self.state, "arrayVal");?

2.7.2 字典傳遞
字典的傳遞同樣需要先入棧一個(gè)Table:
lua_newtable(self.state);
然后對(duì)要傳遞的字典進(jìn)行遍歷,并通過lua_setfield方法將元素設(shè)置到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_setfield與lua_rawseti功能類型,都是把一個(gè)元素放入Table中,只是一個(gè)用于指定整數(shù)索引,一個(gè)是指定字符串索引。通過上面的方式就可以把字典傳遞給Lua了。
2.8 自定義數(shù)據(jù)傳遞
Lua中一個(gè)比較強(qiáng)大的地方是它可以將任意的類型(包括類對(duì)象)進(jìn)行傳遞。特別是在提供原生處理方法時(shí),需要用到一些特定的數(shù)據(jù)類型作為參數(shù)時(shí),Lua就可以幫我們實(shí)現(xiàn)這一塊的傳遞。
要想傳遞自定義的數(shù)據(jù)則必須要使用Lua提供的Userdata類型。該類型有兩種引用方式,一種是強(qiáng)引用Userdata,由Lua的GC來負(fù)責(zé)該類型變量的生命周期。另外一種是弱引用Userdata,又稱Light Userdata,該類型不被GC所管理,其生命周期由原生層來決定。下面來看一下兩種方式是如何實(shí)現(xiàn)的。
首先我們來定義一個(gè)OC的User類:
@interface User : NSObject
@property (nonatomic, copy) NSString *name;
@end
利用lua_newuserdata方法來創(chuàng)建一個(gè)強(qiáng)引用Userdata,并創(chuàng)建一個(gè)User對(duì)象賦值給新建的Userdata。如:
void *instanceRef = lua_newuserdata(self.state, sizeof(User *));
instanceRef = (__bridge_retained void *)[[User alloc] init];
lua_setglobal(self.state, "userdataVal");
通過上面的代碼就可以把User類實(shí)例封裝成Userdata再傳遞給Lua。如果你要傳遞的對(duì)象并不需要Lua來管理生命周期,那么就可以創(chuàng)建一個(gè)弱引用的Userdata,如:
User *user = [[User alloc] init];
lua_pushlightuserdata(self.state, (__bridge void *)(user));
lua_setglobal(self.state, "userdataVal");
例子
1.//定義c函數(shù)
static int printUser (lua_State *state){
? ? if (lua_gettop(state) > 0)?{
? ? ? ? //表示有參數(shù)傳入
? ? ? ? User *user = (__bridge User *)(lua_topointer(state, 1));
? ? ? ? NSLog(@"user.name = %@", user.name);
? ? }
? ? return 0;
}
2.該方法通過lua_topointer方法來獲取了一個(gè)Userdata數(shù)據(jù)類型并轉(zhuǎn)換為User類實(shí)例對(duì)象然后打印其名稱。接下來將其導(dǎo)出給Lua:
lua_pushcfunction(self.state, printUser);
lua_setglobal(self.state, "printUser");?//把printUser函數(shù)暴露給lua
3.然后生成一個(gè)User對(duì)象,并調(diào)用該方法傳入該用戶對(duì)象。如:
//創(chuàng)建User對(duì)象
User *user = [[User alloc] init];
user.name = @"vimfung";
lua_getglobal(self.state, "printUser"); //把lua中的printUser函數(shù)放入棧中
//傳入U(xiǎn)ser參數(shù)
lua_pushlightuserdata(self.state, (__bridge void *)(user)); //把user對(duì)象放入棧中
lua_pcall(self.state, 1, 0, 0); //表示調(diào)用棧中的函數(shù),第二個(gè)參數(shù)為函數(shù)參數(shù)的數(shù)量,第三個(gè)是返回值的數(shù)量,第四個(gè)參數(shù)是用于發(fā)生錯(cuò)誤處理時(shí)的代碼返回
最終的輸出信息如下:user.name = vimfung
3.From Lua to OC
3.1 獲取數(shù)值
lua_getglobal(self.state, "aa"); //從c空間到棧上
double value = lua_tonumber(self.state, -1); //從棧到oc空間
NSLog(@"aa = %f", value);
lua_pop(self.state, 1);
lua_getglobal方法是用于獲取全局變量的值,調(diào)用它會(huì)把一個(gè)值放入棧頂。然后再通過lua_tonumber把棧頂?shù)闹底x取出來并打印。需要注意的是通過lua_getglobal獲取得到的值,在棧中是不會(huì)自動(dòng)清除,因此,在用完某個(gè)變量時(shí)記得把它從棧中清除掉,代碼中是通過lua_pop把值彈出棧的。
3.2 獲取布爾值
與獲取數(shù)值相同,通過lua_toboolean方法來獲取某個(gè)布爾變量的值。如:
lua_getglobal(self.state, "aa");
BOOL value = lua_toboolean(self.state, -1);
if (value){
? NSLog(@"aa = YES");
}
else{
? NSLog(@"aa = NO");
}
lua_pop(self.state, 1);
3.3 獲取字符串
lua_tostring方法來獲取字符串變量的值。如:
lua_getglobal(self.state, "aa");
const char *value = lua_tostring(self.state, -1);
NSLog(@"aa = %s", value);
lua_pop(self.state, 1);
3.4 獲取二進(jìn)制數(shù)組
lua_tolstring方法來獲二進(jìn)制數(shù)組變量的值。如:
lua_getglobal(self.state, "aa");
size_t len = 0;
const char *bytes = lua_tolstring(self.state, -1, &len);
NSData *data = [NSData dataWithBytes:bytes length:len];
NSLog(@"aa = %@", data);
lua_pop(self.state, 1);
3.5 方法的獲取和調(diào)用
一般情況下,要獲取Lua中的某個(gè)Function主要是用于對(duì)其進(jìn)行調(diào)用。假設(shè)有一個(gè)Lua方法定義如下:
function printHelloWorld()?
?print("Hello World");
end
那么,OC中則可以進(jìn)行下面操作來調(diào)用方法并傳遞參數(shù),最終取得返回值然后打印到控制臺(tái):
lua_getglobal(self.state, "printHelloWorld");
lua_pcall(self.state, 0, 0, 0);
上述代碼中的lua_pcall方法表示將棧中的元素視作Function來進(jìn)行調(diào)用。其中第二個(gè)參數(shù)為傳入?yún)?shù)的數(shù)量,必須與壓棧的參數(shù)數(shù)量一致;第三個(gè)參數(shù)為返回值的數(shù)量,表示調(diào)用后其放入棧中的返回值有多少個(gè)。第四個(gè)參數(shù)是用于發(fā)生錯(cuò)誤處理時(shí)的代碼返回。其運(yùn)行原理如下圖所示:

大概過程是:lua_getglobal先把lua函數(shù)入棧,在oc中把要傳給lua的參數(shù)也入棧;然后lua_pcall將棧中的元素視作Function來進(jìn)行調(diào)用,調(diào)用完畢后清空棧,然后將lua函數(shù)的返回值放回棧中
舉例說明
假設(shè)有一個(gè)加法的Lua方法,其定義如下:
function add(a, b)
?return a + b;
end
那么,OC中則可以進(jìn)行下面操作來調(diào)用方法并傳遞參數(shù),最終取得返回值然后打印到控制臺(tái):
lua_getglobal(self.state, "add");
lua_pushinteger(self.state, 1000);
lua_pushinteger(self.state, 24);
lua_pcall(self.state, 2, 1, 0);
NSInteger retVal = lua_tonumber(self.state, -1);
NSLog(@"retVal = %ld", retVal);
3.6 Table的獲取和遍歷
Table的獲取跟其他變量一樣,一旦放入棧后可以根據(jù)需要通過調(diào)用lua_getfield方法來指定的key的值。如:
//假設(shè)Lua中有一個(gè)Table變量aa = {key1=1000, key2=24};
lua_getglobal(self.state, "aa");
lua_getfield(self.state, -1, "key2");
NSInteger value = lua_tonumber(self.state, -1);
NSLog(@"value = %ld", value);
lua_pop(self.state, 1);
如果Table是聲明時(shí)沒有指定key,那么則需要調(diào)用lua_rawgeti來獲取Table的值。如:
//假設(shè)Lua中有一個(gè)Table變量aa = {1000, 24};
lua_getglobal(self.state, "aa");
lua_rawgeti(self.state, -1, 2);
NSInteger value = lua_tonumber(self.state, -1);
NSLog(@"value = %ld", value);
lua_pop(self.state, 1);
有時(shí)候,Table存儲(chǔ)的信息會(huì)在函數(shù)體外被訪問,那么我們需要對(duì)Table進(jìn)行遍歷然后把它放入一個(gè)字典中,然后提供給程序使用。代碼如下:
//假設(shè)Lua中有一個(gè)Table變量aa = {1000, 24};
lua_getglobal(self.state, "aa");
lua_pushnil(self.state);
while (lua_next(self.state, -2)){
? NSInteger value = lua_tonumber(self.state, -1);
? if (lua_type(self.state, -2) == LUA_TSTRING) {
? ? const char *key = lua_tostring(self.state, -2);
? ? NSLog(@"key = %s, value = %ld", key, value);
? }
? else if (lua_type(self.state, -2) == LUA_TNUMBER) {
? ? NSInteger key = lua_tonumber(self.state, -2);
? ? NSLog(@"key = %ld, value = %ld", key, value);
? }
? lua_pop(self.state, 1);
}
上述代碼利用lua_next方法來遍歷Table的所有元素,該方法從棧頂彈出一個(gè)元素作為遍歷Table的起始Key,然后把每個(gè)元素的Key和Value放入棧中。為了遍歷所有元素所以起始的Key設(shè)置了一個(gè)nil值,證明要從Table最開始的Key進(jìn)行遍歷。如圖:

值得注意的是,在獲取Key值時(shí),最好先判斷Key的類型,然后再根據(jù)其對(duì)應(yīng)類型調(diào)用相應(yīng)的lua_toXXX方法。否則,因?yàn)閘ua_toXXX系列方法會(huì)對(duì)元素值進(jìn)行類型轉(zhuǎn)換,如整型的Key被lua_tostring轉(zhuǎn)換為String后再給到lua_next進(jìn)行遍歷就會(huì)報(bào)找不到指定Key的錯(cuò)誤。