Lua 中使用面向对象(续)--newproxy生成userdata使用__gc
来源:互联网 发布:java pfx证书读取私钥 编辑:程序博客网 时间:2024/05/16 07:34
转自http://www.cnblogs.com/yaukey/p/4568202.html
上一篇文章给了一个面向对象的方案,美中不足的是没有析构函数 Destructor,那么这一次就给它加上。
既然是析构,那么就是在对象被销毁之前做该做的事情,lua 5.1 的 userdata 可以给其 metatable 增加一个 __gc 域,指定一个函数,将会在被回收时调用,这个 __gc 只能用于 userdata,普遍的 table 不支持;到了 lua 5.2 以后,官方支持了给予普通 table 的 metatable 增加 __gc 域,可以在回收前被回调;具体细节可以参考对应版本的 manual。
userdata 一般是 c 里创建的自定义数据结构,但是如果想在 lua 里做这件事情的该如何实现,理论上 lua 是不支持的,但是作者增加了一个隐藏的非公开测试函数 newproxy 用于创建一个空的 userdata,参数可以选择是否带 metatable。用法如下:
- newproxy<span></span><strong>--如果参数设置为true则返回一个userdate带一个空的metatable,如果参数一个newproxy产生的userdata,
- 则返回一个共用传入参数的metatable的userdata。如果参数是false,返回一个没有metatable的userdata
- </strong>newproxy (boolean or proxy)
- Undocumented feature of Lua.
- Arguments: boolean - returned proxy has metatable or userdata - different proxy created with newproxy
- Creates a blank userdata with an empty metatable, or with the metatable of another proxy. Note that, in ROBLOX, creating
- a proxy with another proxy is disabled and will error.
-
- local a = newproxy(true)
- local mt = getmetatable(a)
- print( mt ~= nil )
-
- local b = newproxy(a)
- print( mt == getmetatable(b) )
-
- local c = newproxy(false)
- print( getmetatable(c) ~= nil )
-
- mt.__index = {Name="Proxy"}
- print( a.Name )
- print( b.Name )
-
- -- Output:
- true
- true
- false
- Proxy
- Proxy
使用它就可以创建一个空的 userdata 并指定 __gc 操作,在你的对象上保持一个唯一的引用到该 userdata,当你的对象被销毁前 userdata 的 __gc 会被调用。
对于 5.2 及以后版本的 table 的 __gc,需要注意,你必须在第一次为其设置 metatable 时就制定 __gc,才能开启改标记,否则先设置 metatable,而到其后修改 metatable,增加 __gc 域,是不起作用的。
下面给出改进过的面向对象方案,注意这里做了版本区分,TxClass:
- -- Get current version number.
- local _, _, majorv, minorv, rev = string.find(_VERSION, "(%d).(%d)[.]?([%d]?)")
- local VersionNumber = tonumber(majorv) * 100 + tonumber(minorv) * 10 + (((string.len(rev) == 0) and 0) or tonumber(rev))
-
- -- Declare current version number.
- TX_VERSION = VersionNumber
- TX_VERSION_510 = 510
- TX_VERSION_520 = 520
- TX_VERSION_530 = 530
-
- -- The hold all class type.
- local __TxClassTypeList = {}
-
- -- The inherit class function.
- local function TxClass(TypeName, SuperType)
- -- Create new class type.
- local ClassType = {}
-
- -- Set class type property.
- ClassType.TypeName = TypeName
- ClassType.Constructor = false
- ClassType.SuperType = SuperType
-
- -- The new alloc function of this class.
- ClassType.new = function (...)
- -- Create a new object first and set metatable.
- local Obj = {}
-
- -- Give a tostring method.
- Obj.ToString = function (self)
- local str = tostring(self)
- local _, _, addr = string.find(str, "table%s*:%s*(0?[xX]?%x+)")
- return ClassType.TypeName .. ":" .. addr
- end
-
- -- Do constructor recursively.
- local CreateObj = function (Class, Object, ...)
- local Create
- Create = function (c, ...)
- if c.SuperType then
- Create(c.SuperType, ...)
- end
-
- if c.Constructor then
- c.Constructor(Object, ...)
- end
- end
-
- Create(Class, ...)
- end
-
- -- Do destructor recursively.
- local ReleaseObj = function (Class, Object)
- local Release
- Release = function (c)
- if c.Destructor then
- c.Destructor(Object)
- end
-
- if c.SuperType then
- Release(c.SuperType)
- end
- end
-
- Release(Class)
- end
-
- -- Do the destructor by lua version.
- if TX_VERSION < TX_VERSION_520 then
- -- Create a empty userdata with empty metatable.
- -- And mark gc method for destructor.
- local Proxy = newproxy(true)
- getmetatable(Proxy).__gc = function (o)
- ReleaseObj(ClassType, Obj)
- end
-
- -- Hold the one and only reference to the proxy userdata.
- Obj.__gc = Proxy
-
- -- Set metatable.
- setmetatable(Obj, {__index = __TxClassTypeList[ClassType]})
- else
- -- Directly set __gc field of the metatable for destructor of this object.
- setmetatable(Obj,
- {
- __index = __TxClassTypeList[ClassType],
-
- __gc = function (o)
- ReleaseObj(ClassType, o)
- end
- })
- end
-
- -- Do constructor for this object.
- CreateObj(ClassType, Obj, ...)
- return Obj
- end
-
- -- Give a ToString method.
- ClassType.ToString = function (self)
- return self.TypeName
- end
-
- -- The super class type of this class.
- if SuperType then
- ClassType.super = setmetatable({},
- {
- __index = function (t, k)
- local Func = __TxClassTypeList[SuperType][k]
- if "function" == type(Func) then
- t[k] = Func
- return Func
- else
- error("Accessing super class field are not allowed!")
- end
- end
- })
- end
-
- -- Virtual table
- local Vtbl = {}
- __TxClassTypeList[ClassType] = Vtbl
-
- -- Set index and new index of ClassType, and provide a default create method.
- setmetatable(ClassType,
- {
- __index = function (t, k)
- return Vtbl[k]
- end,
-
- __newindex = function (t, k, v)
- Vtbl[k] = v
- end,
-
- __call = function (self, ...)
- return ClassType.new(...)
- end
- })
-
- -- To copy super class things that this class not have.
- if SuperType then
- setmetatable(Vtbl,
- {
- __index = function (t, k)
- local Ret = __TxClassTypeList[SuperType][k]
- Vtbl[k] = Ret
- return Ret
- end
- })
- end
-
- return ClassType
- end
使用也很简单:
- local MyBase = TxClass("MyBase")
- function MyBase:Constructor()
- print("MyBase:Constructor")
- end
-
- function MyBase:Destructor()
- print("MyBase:Destructor")
- end
-
- local MyNew = TxClass("MyNew", MyBase)
-
- function MyNew:Constructor()
- print("MyNew:Constructor")
- end
-
- function MyNew:Destructor()
- print("MyNew:Destructor")
- end
-
- local cls = MyNew()
- cls = nil
- collectgarbage()
-
- -- Output:
- MyBase:Constructor
- MyNew:Constructor
- MyNew:Destructor
- MyBase:Destructor
接下来的扩展是,给一个简单的运行时方法:IsA。