前言
复习下Lua元表相关的知识
正文
定义
- 元表
元表可以修改一个值在面对一个未知操作时的行为,是一个操作行为拓展的集合,常常用于拓展table的行为,也可用于userdata。 - 元方法
可以理解为元表中的某个字段指向的拓展函数或者拓展表。
常用元方法字段
__index
当访问一个表中不存在的字段时,如果该表存在元表,那么解释器会尝试去查找该元方法,并由这个元方法提供最终结果。
应用:实现面向对象的继承
我们可以用元表的__index元方法来实现一个简单的单继承机制。
- 类的继承实现
1 | local definedClasses = {} |
- 构建一个
a<-b<-c的继承链。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21local a = class("parent")
function a:ctor()
print("ctor a")
end
function a:Hello()
print("parent Hello")
end
local b = class("child", a)
function b:Hello()
print("Child Hello ")
end
function b:ctor(...)
print("ctor b")
end
local c = class("grandson", b) - 构建三个类的实例化对象进行测试。
1
2
3
4
5
6local insA = a:new()
local insB = b:new()
local insC = c:new()
insA:Hello()
insB:Hello()
insC:Hello() - 输出如下结果,可以看到子类
C发现自己的表中没有Hello这个字段,就通过元表索引到了B的同名方法进行了调用。
__newindex
这个元方法会在向表内一个不存在的键索引赋值时触发。
应用:使用__newindex跟踪表的赋值操作
测试代码
1 |
|
输出如下
需要注意的是在track_func部内部使用的rawset来替代t[key] = val,从而避免赋值操作重复触发__newindex的递归死循环。
其他元方法
其他的元方法示例可以参见此处。
所有元方法字段参见Lua Wiki的MetatableEvents章节。
MetaTable的操作源码
1 | static int luaB_getmetatable (lua_State *L) { |
从源码中还可以发现一些元表的使用trick。
- 可以设置某张表的
__metatable为非空字段来屏蔽元表功能或者保护已设置好的元表。 - Lua的
C API除了可以对Table和UserData设置元表外,还可以对其他类型也设置一个全局的元表。 - 设置元表会触发
LuaGC的屏障机制来避免对应的表在当前可能处于GC回收阶段的情况下被回收掉。 - 函数
setmetatable是有返回值的,值为设置metatable的table。
总结
- 元表非常适合用来拓展自己想要的额外逻辑。
XLua的Lua侧访问Unity和C#的对象机制就是通过UserData的元表机制来实现的。 - 元表的设计思路很符合设计模式中的观察者模式,元表的各种字段就相当于各种操作事件,元表就相当于这一系列操作事件的订阅者,Lua虚拟机执行到代码特定位置时会触发相应的事件来调用元表中的元方法。