1.模块与包
-
基础知识
- 模块类似于一个封装库,从 Lua 5.1 开始,Lua 加入了标准的模块管理机制,可以把一些公用的代码放在一个文件里,以 API 接口的形式在其他地方调用,有利于代码的重用和降低代码耦合度。
- Lua 的模块是由变量、函数等已知元素组成的 table。
local module = {}
-- 定义一个常量
module.constant = "这是一个常量"
-- 定义一个函数
function module.func1()
io.write("这是一个公有函数!\n")
end
local function func2()
print("这是一个私有函数!")
end
return module
- Lua提供了一个名为require的函数用来加载模块。 require()函数先检测package.loaded表中是否存在module_name,若存在则直接返回当中的值,若不存在则通过定义的加载器加载module_name。
function require(module)
-- 判断模块是否已经被加载
if not package.loaded[module] then
-- 获取模块的加载器
local loader = findloader(module)
if loader==nil then
error("unable to load module "..module)
end
-- 将模块标记为已加载
package.loaded[module] = true
-- 初始化模块
local result = loader(module)
if result~=nil then
package.loaded[module] = result
end
end
return package.loaded[module]
end
- Lua提供require()函数用来加载运行库,require()和dofile()完成相同的功能,不同点是
require会搜索目录以加载文件
require不会重复加载同一模块
若要让每次加载文件都执行,可使用dofile。
若加载后不执行,等需要时执行,可使用loadfile。
问题
-代码内更新可以很好的利用此机制。
2. 元表与元方法
基础知识
-
在 Lua table 中我们可以访问对应的key来得到value值,但是却无法对两个 table 进行操作。因此 Lua 提供了元表(Metatable),允许我们改变table的行为,每个行为关联了对应的元方法。
-
举例来说,当一个非数字的值作加法操作的时候, Lua 会检查它的 metatable 中 "__add" 域中的是否有一个函数。 如果有这么一个函数的话,Lua 调用这个函数来执行一次加法。
-
我们叫 metatable 中的键名为 事件 (event) ,把其中的值叫作 元方法 (metamethod)。 在上个例子中,事件是 "add" 而元方法就是那个执行加法操作的函数。
-
可以通过
getmetatable
函数来查询到任何一个值的 metatable。 -
可以通过
setmetatable
函数来替换掉 table 的 metatable 。 -
元方法 __index 用来对表访问,访问规则:
1.在表中查找,如果找到,返回该元素,找不到则继续。
2.判断该表是否有元表,如果没有元表,返回 nil,有元表则继续。
3.判断元表有没有 __index 方法,如果 __index 方法为 nil,则返回 nil;如果 __index 方法是一个表,则重复 1、2、3;如果 __index 方法是一个函数,则返回该函数的返回值。 -
元方法 __newindex 用来对表更新,更新规则:
1.如果是表已存在的索引键,则直接更新,没有的话就继续。
2.解释器就会查找__newindex 元方法:如果存在则调用这个函数而不进行赋值操作。
3.如果不存在则直接对表赋值。
问题
- 因为Lua本身是没有面向对象支持的,但在项目开发中需要面向对象编程,于是很多人用Lua本身的数据结构table+元表来模拟面向对象实现类、继承、多重继承。当元方法
__index
和__newindex
变成函数时,会因为函数本身的复杂度导致逻辑消耗时间变多
。在大型项目里是不小的性能开销。
3. 弱引用Table
基础知识
- Lua有着自己的自动内存管理。程序只需要负责创建对象,而不需要去删除对象。通过垃圾回收机制,lua会自动去删除那些已经成为垃圾的对象。但问题在垃圾收集器只能回收那些它认为是垃圾的东西,不会回收那些用户认为是垃圾的东西。比如那些存储在全局变量中的对象,即使程序不会再用到它们,但对于Lua来说它们也不是垃圾,除非用户将这些对象赋值为nil,这样它们才能被释放。但有时候,简单地清除引用还不够,比如将一个对象放在一个数组中时,它就无法被回收,这是因为即使当前没有其他地方在使用它,但数组仍引用着它,除非用户告诉Lua这项引用不应该阻碍此对象的回收,否则Lua是无从得知的。
- table中有key和value,这两者都可以包含任意类型的对象。通常,垃圾收集器不会回收一个可访问table中作为key或value的对象。也就是说,这些key和value都是强引用,它们会阻止对其所引用对象的回收。在一个弱引用table中,key和value是可以回收的。
- 弱引用table(weak table)是用户用来告诉Lua一个引用不应该阻碍对该对象的回收。所谓弱引用,就是一种会被垃圾收集器忽视的对象引用。如果一个对象的引用都是弱引用,该对象也会被回收,并且还可以以某种形式来删除这些弱引用本身。
对于弱引用table,其实有三种形式:
1.key值弱引用,也就是刚刚说到的情况,只要其他地方没有对key值引用,那么,table自身的这个字段也会被删除。设置方法:setmetatable(t, {__mode = “k”});
2.value值弱引用,情况类似,只要其他地方没有对value值引用,那么,table的这个value所在的字段也会被删除。设置方法:setmetatable(t, {__mode = “v”});
3.key和value弱引用,规则一样,但是key和value都同时生效,任意一个起作用时都会导致table的字段被删除。设置方法:setmetatable(t, {__mode = “kv”});
local t = {};
--setmetatable(t, {__mode = "k"}) --key值弱引用
--setmetatable(t, {__mode = "v"}) --value值弱引用
--setmetatable(t, {__mode = "kv"}) --key和value弱引用
-- 使用一个table作为t的key值
local key = {name = "key"}
t[key] = key
key = nil
-- 使用一个table作为t的key值
local value = {name = "value"}
t["value"] = value
value = nil
-- 强制进行一次垃圾收集
collectgarbage();
for key, value in pairs(t) do
print(value.name)
end
问题
- 弱引用就是一种这样的机制。实际代码开发中还是强引用的,需要开发者管理好引用关系减少gc。
网友评论