【元表】
元表中的鍵為事件(event),稱值為元方法(metamethod)。
通過函數getmetatable查詢不論什么值的元表,通過函數setmetatable替換表的元表。
setmetatable(僅僅能用於table)和getmetatable(用於不論什么對象)
語法:setmetatable (table, metatable),對指定table設置metatable 【假設元表(metatable)中存在__metatable鍵值。setmetatable會失敗】
語法:tmeta = getmetatable (tab)。返回對象的元表(metatable) 【假設元表(metatable)中存在__metatable鍵值,當返回__metatable的值】
【元方法】
元表能夠控制對象的數學運算、順序比較、連接、取長、和索引操作的行為。
當Lua對某值運行當中一個操作時。檢查該值是否含有元表以及對應的事件。
假設有,與該鍵關聯的值(元方法)控制Lua怎樣完畢操作。
每一個操作的鍵是由其名字前綴兩個下划線“__”的字符串。比如。操作“加(add)”的鍵是字符串"__add"。
特別一提,要獲取給定對象的元方法。我們使用表達式
metatable(obj)[event]
它應被解讀為
rawget(getmetatable(obj) or {}, event)
就是說,訪問一個元方法不會調用其它元方法,並且訪問沒有元表的對象不會失敗(僅僅是結果為nil)。
"add": + 操作。
以下的getbinhandler函數定義Lua怎樣選擇二元操作的處理程序。首先嘗試第一操作數,假設它的類型未定義該操作的處理程序。則嘗試第二操作數。
function getbinhandler (op1, op2, event)
return metatable(op1)[event] or metatable(op2)[event]
end
事件:
add
sub
mul
div
mod
pow
unm 一元操作-
concat 連接操作..
len 取長操作#
eq 同樣操作==
lt 小於操作<
le
index 索引訪問table[key],參數table,key
newindex 索引賦值table[key] = value, 參數table, key, value
call lua調用
__index元方法:
依照之前的說法,假設A的元表是B,那么假設訪問了一個A中不存在的成員。就會訪問查找B中有沒有這個成員。這個過程大體是這樣,但卻不全然是這樣。實際上,即使將A的元表設置為B。並且B中也確實有這個成員,返回結果仍然會是nil。原因就是B的__index元方法沒有賦值。依照我的理解,__index方法是用來確定一個表在被作為元表時的查找方法。
【用戶數據和元方法】
Userdata:
A userdata offers a row memory area, with no predefined operations in Lua, which we can use to store anything (分配指定數量的內存在棧上。把數據已用戶自己定義的數據結構存放進去)
lua_newuserdata() -- allocates a block of memory with the given size, pushes the corresponding userdatum on the stack, and returns the block address
Metatable:
The usual method to distinguish one type of userdata from other userdata is to create a unique metatable for that type.
Lua code cannot change the metatable of a userdatum, it cannot fake our code (Lua 不能改變userdata里面的metatable,所以userdata的metatable能夠用作唯一標示符來識別userdata,這里metatable拿來推斷是否傳入了正確的userdata參數)
【繼承】
cocos2dx里的繼承:
function class(classname, ...) local cls = {__cname = classname} local supers = {...} for _, super in ipairs(supers) do local superType = type(super) assert(superType == "nil" or superType == "table" or superType == "function", string.format("class() - create class \"%s\" with invalid super class type \"%s\"", classname, superType)) if superType == "function" then assert(cls.__create == nil, string.format("class() - create class \"%s\" with more than one creating function", classname)); -- if super is function, set it to __create cls.__create = super elseif superType == "table" then if super[".isclass"] then -- super is native class assert(cls.__create == nil, string.format("class() - create class \"%s\" with more than one creating function or native class", classname)); cls.__create = function() return super:create() end else -- super is pure lua class cls.__supers = cls.__supers or {} cls.__supers[#cls.__supers + 1] = super if not cls.super then -- set first super pure lua class as class.super cls.super = super end end else error(string.format("class() - create class \"%s\" with invalid super type", classname), 0) end end cls.__index = cls if not cls.__supers or #cls.__supers == 1 then setmetatable(cls, {__index = cls.super}) else setmetatable(cls, {__index = function(_, key) local supers = cls.__supers for i = 1, #supers do local super = supers[i] if super[key] then return super[key] end end end}) end if not cls.ctor then -- add default constructor cls.ctor = function() end end cls.new = function(...) local instance if cls.__create then instance = cls.__create(...) else instance = {} end setmetatableindex(instance, cls) instance.class = cls instance:ctor(...) return instance end cls.create = function(_, ...) return cls.new(...) end return cls end原理就不細說了。改動__index。使得在訪問其成員的時候能遍歷全部的supers父類去查找該成員(類似js里的原型鏈,但那是一條鏈,這里lua可自由發揮)。
他的第一個參數是類名,后面的參數能夠是父表或者函數,函數的話僅僅能有一個,是用來作為創建函數的__create,會在.new的時候被調用。
用法:
local UIScene = class("UIScene") UIScene.__index = UIScene function UIScene.extend(target) local t = tolua.getpeer(target) if not t then t = {} tolua.setpeer(target, t) end setmetatable(t, UIScene) return target end function UIScene.create() local scene = cc.Scene:create() local layer = UIScene.extend(cc.Layer:create()) layer:init() scene:addChild(layer) return scene endgetpeer/setpeer我還不是非常理解,從網上找到說明留着消化:
Those are tolua functions. The tolua manual (for example here) has explanations for them. tolua.setpeer (object, peer_table) (lua 5.1 only) Sets the table as the object's peer table (can be nil). The peer table is where all the custom lua fields for the object are stored. When compiled with lua 5.1, tolua++ stores the peer as the object's environment table, and uses uses lua_gettable/settable (instead of lua_rawget/set for lua 5.0) to retrieve and store fields on it. This allows us to implement our own object system on our table (using metatables), and use it as a way to inherit from the userdata object. Consider an alternative to the previous example: -- a 'LuaWidget' class LuaWidget = {} LuaWidget.__index = LuaWidget function LuaWidget:add_button(caption) -- add a button to our widget here. 'self' will be the userdata Widget end local w = Widget() local t = {} setmetatable(t, LuaWidget) -- make 't' an instance of LuaWidget tolua.setpeer(w, t) -- make 't' the peer table of 'w' set_parent(w) -- we use 'w' as the object now w:show() -- a method from 'Widget' w:add_button("Quit") -- a method from LuaWidget (but we still use 'w' to call it) When indexing our object, the peer table (if present) will be consulted first, so we don't need to implement our own __index metamethod to call the C++ functions. tolua.getpeer (object) (lua 5.1 only) Retrieves the peer table from the object (can be nil).