Lua 自動管理內存(垃圾回收),但某些對象除內存外還佔用系統資源:文件描述符、窗口句柄、網絡套接字等。這些資源由操作系統管理,Lua GC 無法自動釋放,需顯式清理。
__gc 域,Lua 調用該函數opendir() 返回 DIR*,必須配對 closedir()dir 函數在局部變量保存 DIR*,遍歷結束後關閉DIR* 必須跨多次調用存活break/報錯),closedir() 不會被調用 → 資源洩漏#include
#include
static int dir_iter(lua_State *L); // 前向聲明
// dir 工廠函數
static int l_dir(lua_State *L) {
const char *path = luaL_checkstring(L, 1);
// 1. 創建 userdata 存儲 DIR*(關鍵:先創建 userdata 再打開目錄)
DIR **d = (DIR**)lua_newuserdata(L, sizeof(DIR*));
// 2. 設置 metatable(含 __gc)
luaL_getmetatable(L, "LuaBook.dir");
lua_setmetatable(L, -2);
// 3. 打開目錄
*d = opendir(path);
if (*d == NULL)
luaL_error(L, "cannot open %s: %s", path, strerror(errno));
// 4. 創建閉包迭代器(userdata 作為 upvalue)
lua_pushcclosure(L, dir_iter, 1);
return 1;
}
// 迭代器函數
static int dir_iter(lua_State *L) {
DIR *d = *(DIR**)lua_touserdata(L, lua_upvalueindex(1));
struct dirent *entry = readdir(d);
if (entry != NULL) {
lua_pushstring(L, entry->d_name);
return 1;
}
return 0; // 結束迭代
}
// __gc 元方法:自動關閉目錄
static int dir_gc(lua_State *L) {
DIR *d = *(DIR**)lua_touserdata(L, 1);
if (d) closedir(d); // 安全檢查:opendir 可能失敗
return 0;
}
// 庫註冊
int luaopen_dir(lua_State *L) {
luaL_newmetatable(L, "LuaBook.dir");
lua_pushstring(L, "__gc");
lua_pushcfunction(L, dir_gc);
lua_settable(L, -3);
lua_pushcfunction(L, l_dir);
lua_setglobal(L, "dir");
return 0;
}
__gc 中需檢查指針有效性__gc 存放在專用 metatable,防止惡意調用for fname in dir(".") do
print(fname)
end
-- 即使 break 或報錯,DIR* 也會被自動 closedir()
將介紹綁定 Expat XML 解析器的更複雜資源管理案例,涉及多層資源依賴與錯誤處理。