lua元表

lua的任何值都可以設(shè)置元表畦徘,其中table用 setmetatable, 其它類型使用debug.setmetatable.
通過(guò)這個(gè)阐肤,可以獲得跟cpp operator重載一樣的騷操作。比如浑塞,可以給一個(gè)number 設(shè)置一個(gè)元表借跪,實(shí)現(xiàn)__add方法,但是__add中用減法酌壕,這樣掏愁,當(dāng)一個(gè)number被設(shè)置元表后,進(jìn)行加法運(yùn)算卵牍,可以得到減法的結(jié)果果港。


元表的key分類

  1. 運(yùn)算
    __add,__div,__idiv,__mul,__sub,__pow,__unm,__mod
  2. 二進(jìn)制運(yùn)算
    __band,__bnot,__bor,__bxor,__shl,__shr
  3. 比較
    __eq,__lt,__le
  4. 字符串相關(guān)
    __name,__tostring,__concat
  5. 表操作
    __len,__index,__newindex,__pairs
  6. 其它
    __call,__close,__gc,__mode,__metatable

詳細(xì)解釋用法

通用類

運(yùn)算/二進(jìn)制運(yùn)算/比較/__concat
這幾個(gè)都是比較簡(jiǎn)單的,函數(shù)類似:function(lvalue,rvalue) return xxx end 這樣的

字符串相關(guān)
__name
  1. 下面是一個(gè)簡(jiǎn)單的例子
local t = {id=1}
local mt = {
    __name="newTypeName",
}
setmetatable(t,mt)
print(type(t),t)  --輸出   table   newTypeName: 0x55a032ead330
  1. 但是更經(jīng)常的是在c中使用
int luaL_newmetatable (lua_State *L, const char *tname);

這時(shí)會(huì)在 registry 創(chuàng)建一個(gè){__name=tname}的表糊昙,并在 registry[tname]=new table. 并將這個(gè)new table TODO 這里要看看是不是

__tostring

print(t)的時(shí)候辛掠,直接打印出來(lái)

local t = {id=1}
local mt = {
    __tostring=function(_t) return "id=".._t.id end
}
setmetatable(t,mt)
print(type(t),t)--table   id=1
表操作
__len
  1. 對(duì)非string/table調(diào)用時(shí)會(huì)查看有沒有__len這個(gè)函數(shù),如果沒有就報(bào)錯(cuò)
__index,__newindex

__index為 t[xxx] 獲取操作释牺,當(dāng)不存在時(shí)調(diào)用函數(shù)或獲取table
__newindex為 t[xxx]=value 設(shè)置操作萝衩,當(dāng)不存在key時(shí)調(diào)用函數(shù)

__pairs
  1. 可以重定義table的pairs ,如果是其它類型没咙,也可以定義猩谊。__pairs對(duì)應(yīng)的函數(shù)需要返回 function,傳入的參數(shù)1,nil,
    簡(jiǎn)單例子如下
local function testxx()
    local s = "a string"

    local mt = {
        __pairs = function(_s)
            return function(_ts,_idx)
                local len = #_ts
                _idx = _idx or 0
                _idx = _idx + 1
                if _idx <= len then
                    return _idx,string.sub(_ts,_idx,_idx)--這里先return idx
                end
                return nil,nil
            end,_s,nil
        end
    }
    debug.setmetatable(s,mt)

    for k,v in pairs(s) do
        print(k,v)
    end
end
testxx()

輸出如下


a.png
其它
__call

使非function能像函數(shù)一樣調(diào)用

local t = {id=2}
setmetatable(t,{
        __call = function(a,arg1)
            return a.id+arg1
        end
    })

print(t(10))
--輸出
--12
__close
  1. 在lua5.4中,基于 __gc的調(diào)用時(shí)機(jī)不明確镜撩,__close是專門用于一個(gè)變量逃出作用域時(shí)被調(diào)用的函數(shù)预柒。正常退出及 break/goto/return 及 拋出錯(cuò)誤時(shí),都會(huì)被調(diào)用袁梗∫搜欤可以看作是golang的defer ,c#的using語(yǔ)句,使用例子如下
local function createXX()
    local ret = {}
    local mt = {__close=function(t,errObj)
        print("close to-be-closed variable")
    end}
    setmetatable(ret,mt)
    return ret
end

do
    local x<close> = createXX()
end
  1. 其它特性:
    2.1. 順序跟__gc的一樣,是倒序
    2.2. 如果__close函數(shù)拋出異常遮怜,則會(huì)被外面捕獲
local function createXX()
    local ret = {}
    local mt = {__close=function(t,errObj)
        print("close to-be-closed variable",t.id)
        assert(false,"hhhh")
    end}
    setmetatable(ret,mt)
    return ret
end

do
    local x<close> = createXX()
    x.id = 2

    local a<close> = createXX()
    a.id = 3
end

輸出如下


close1.png

2.3 如果一個(gè)coroutine 被 yield 而且從來(lái)沒有被resume淋袖,則不會(huì)被close

local function testCoroutineClose()
    local function createV()
        local ret = {}
        local mt = {__close=function(t,errObj)
            print("close",t.id)
        end}
        setmetatable(ret,mt)
        return ret
    end


    local co = coroutine.create(function()
        do
            local a1<close> = createV()
            a1.id = 1
        end
        coroutine.yield(1,2)
        do
            local a1<close> = createV()
            a1.id = 2 --因?yàn)闆]有resume,所以不會(huì)調(diào)用close
        end
        return 3,4
    end)
    coroutine.resume(co)
    --coroutine.resume(co)
end
testCoroutineClose()
--輸出
--close    1

2.4. 如果一個(gè)coroutine以error結(jié)束锯梁,則因?yàn)闆]有清空stack即碗,所以也不會(huì) close 任何變量 這時(shí)需要 __gc 或 coroutine.close來(lái)實(shí)現(xiàn)回收。當(dāng)然陌凳,如果coroutine 是用 coroutine.wrap來(lái)創(chuàng)建剥懒,那么即使是錯(cuò)誤也會(huì)有close被調(diào)用

local function testCoroutineClose()
    local function createV()
        local ret = {}
        local mt = {__close=function(t,errObj)
            print("close",t.id)
        end}
        setmetatable(ret,mt)
        return ret
    end


    local co = coroutine.create(function()
        do
            local a1<close> = createV()
            a1.id = 1
        end
        local a<close> = createV()
        a.id=2
        coroutine.yield(1,2)
        do
            local a1<close> = createV()
            a1.id = 3
        end
        return 3,4
    end)
    coroutine.resume(co)
    coroutine.close(co)
end
testCoroutineClose()
--輸出
--close    1
--close    2
__gc
  1. 總述 當(dāng)一個(gè)object( table/userdata) 在被gc認(rèn)為要回收的時(shí)候會(huì)被調(diào)用,因?yàn)槔厥諘?huì)發(fā)生在進(jìn)程的任意階段合敦,所以__gc也可能在進(jìn)程的任意階段被調(diào)用初橘,在lua_close時(shí),肯定會(huì)調(diào)用,常用于管理資源比如文件,網(wǎng)絡(luò),數(shù)據(jù)庫(kù)鏈接,或lightuserdata.
  2. __gc這個(gè)值需要在 setmatatable 函數(shù)調(diào)用之前就已經(jīng)有這個(gè)key了,如果先調(diào)用setmetatable保檐,后再將mt加上__gc這個(gè)key對(duì)應(yīng)的函數(shù)耕蝉,那么是無(wú)效的
 local function test1()
     local t = {}
     local mt = {}
     setmetatable(t,mt)
     mt.__gc = function(t)
         print("this line not print")
     end
 end
 test1()
  1. 當(dāng)垃圾回收認(rèn)為一個(gè)object需要回收的時(shí)候,不會(huì)馬上對(duì)內(nèi)存進(jìn)行回收夜只,而是先把這個(gè)object放到一個(gè)list上去垒在,然后再將從list中逐一檢查有沒有 __gc這個(gè)函數(shù),如果有扔亥,就調(diào)用场躯。 另list好像是從尾插向頭的,第一個(gè)調(diào)用__gc的object是最后
local tdmt = {
     __gc = function(t)
         print(t.id)
     end
 }
 local function topdowntest1()
     for i=1,3,1 do
         setmetatable({id=i},tdmt)
     end
     collectgarbage("collect")
 end

 topdowntest1() 
-- 輸出
--3
--2
--1
  1. 因?yàn)樵?垃圾回收的cycle中砸王,需要使用這個(gè)object, 所以對(duì)內(nèi)存回收是在下一個(gè) 垃圾回收 cycle.所以如果你在__gc這個(gè)函數(shù)中推盛,把這個(gè)t又引用到別的地方去(比如global) 那么峦阁,它就不會(huì)被回收谦铃,但是當(dāng) __gc這個(gè)被調(diào)用之后,不會(huì)再被調(diào)用.
local tdmt = {
     __gc = function(t)
         print(t.id)
     end
 }
 local sgmt = {
     __gc = function(t)
         gv = t
         print("global",t.id)
         setmetatable(gv,tdmt)
     end
 }

 local function resurrected()
     setmetatable({id=4},tdmt)
     setmetatable({id=5},sgmt)
     collectgarbage("collect")
     os.execute("sleep 1")
 end
 resurrected()
//輸出
--global 5
--4
--5
  1. 在__gc函數(shù)中榔昔,除了不能yield之外驹闰,啥都可以,例如:報(bào)錯(cuò)撒会,創(chuàng)object嘹朗,甚至顯示調(diào)用gc. 報(bào)錯(cuò)之后,并不會(huì)傳播出來(lái)诵肛。只是當(dāng)前函數(shù)在報(bào)錯(cuò)之后不會(huì)再執(zhí)行屹培。
local sgmt = {
     __gc = function(t)
         gv = t
         print("global",t.id)
         --assert(false,"xxxx") 如果這句不注釋,則 下面的那句不會(huì)執(zhí)行
         setmetatable(gv,tdmt)
         assert(false,"xxxx") --不會(huì)有任何報(bào)錯(cuò)信息出現(xiàn)
     end
 }

 local function resurrected()
     setmetatable({id=5},sgmt)
     collectgarbage("collect")
     os.execute("sleep 1")
 end
 resurrected()
__mode

被kv修飾過(guò)的怔檩,引用了跟沒引用一樣

local function testmode()
    local t = {}
    setmetatable(t,{__mode="kv"})
    t[1] = {}
    t[2] = {}
    collectgarbage("collect")
    print(next(t)) --打印出nil
    ---
    local c={}
    c[1] = {}
    t[1] = c[1]
    collectgarbage("collect")
    print(next(t)) --有值
    c[1] = nil
    collectgarbage("collect")
    print(next(t))--打印出nil
end

testmode()
  1. 如果k/v 任一weak 只要其中一個(gè)被回收褪秀,如果另一個(gè)只能過(guò)這個(gè)t 來(lái)獲取,事實(shí)上你也取不到另一個(gè)薛训。就是說(shuō)媒吗,如果 key是weak ,但value是strong乙埃,那么如果key被回收闸英,就算value沒有被回收,你也獲取不到value 介袜。
  2. 如果是中間過(guò)程中轉(zhuǎn)變weak為strong,也有可能有一些已經(jīng)被回收(因?yàn)檫@個(gè)改動(dòng)甫何,需要下一個(gè)回收cycle才生效)
  3. 只有會(huì)被gc的value才會(huì)被weak引用刪除。比如table userdata遇伞。string也是會(huì)被gc的辙喂,但是string在這又不會(huì)被weak引用刪除的
local function testmode()
    local t = {}
    setmetatable(t,{__mode="kv"})
    t[1] = {}
    t[2] = {}
    collectgarbage("collect")
    print(next(t))
    local c={}
    c[1] = {}
    t["abc"] = c[1]
    collectgarbage("collect")
    print(next(t))--有值
    c[1] = nil
    collectgarbage("collect")
    print(next(t))
    t["cba"] = "aaaa"
    collectgarbage("collect")
    print(next(t))--有值
end

testmode()
__metatable

都不知道有啥用,不能直接獲取/賦值

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市加派,隨后出現(xiàn)的幾起案子叫确,更是在濱河造成了極大的恐慌,老刑警劉巖芍锦,帶你破解...
    沈念sama閱讀 207,248評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件竹勉,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡娄琉,警方通過(guò)查閱死者的電腦和手機(jī)次乓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)孽水,“玉大人票腰,你說(shuō)我怎么就攤上這事∨” “怎么了杏慰?”我有些...
    開封第一講書人閱讀 153,443評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)炼鞠。 經(jīng)常有香客問(wèn)我缘滥,道長(zhǎng),這世上最難降的妖魔是什么谒主? 我笑而不...
    開封第一講書人閱讀 55,475評(píng)論 1 279
  • 正文 為了忘掉前任朝扼,我火速辦了婚禮,結(jié)果婚禮上霎肯,老公的妹妹穿的比我還像新娘擎颖。我一直安慰自己,他們只是感情好观游,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評(píng)論 5 374
  • 文/花漫 我一把揭開白布搂捧。 她就那樣靜靜地躺著,像睡著了一般备典。 火紅的嫁衣襯著肌膚如雪异旧。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,185評(píng)論 1 284
  • 那天提佣,我揣著相機(jī)與錄音吮蛹,去河邊找鬼。 笑死拌屏,一個(gè)胖子當(dāng)著我的面吹牛潮针,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播倚喂,決...
    沈念sama閱讀 38,451評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼每篷,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼瓣戚!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起焦读,我...
    開封第一講書人閱讀 37,112評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤子库,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后矗晃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體仑嗅,經(jīng)...
    沈念sama閱讀 43,609評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評(píng)論 2 325
  • 正文 我和宋清朗相戀三年张症,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了仓技。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,163評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡俗他,死狀恐怖脖捻,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情兆衅,我是刑警寧澤地沮,帶...
    沈念sama閱讀 33,803評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站涯保,受9級(jí)特大地震影響诉濒,放射性物質(zhì)發(fā)生泄漏周伦。R本人自食惡果不足惜夕春,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望专挪。 院中可真熱鬧及志,春花似錦、人聲如沸寨腔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)迫卢。三九已至倚搬,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間乾蛤,已是汗流浹背每界。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留家卖,地道東北人眨层。 一個(gè)月前我還...
    沈念sama閱讀 45,636評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像上荡,于是被迫代替她去往敵國(guó)和親趴樱。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容

  • 前言 元表對(duì)應(yīng)的英文是metatable,元方法是metamethod叁征。我們都知道纳账,在C++中,兩個(gè)類是無(wú)法直接相...
    胤醚貔貅閱讀 990評(píng)論 0 2
  • 簡(jiǎn)介 模塊庫(kù)類似一個(gè)封裝庫(kù)捺疼,存放公用代碼塞祈,以api接口形式被其他調(diào)用 元表 元表(metatable)提供兩個(gè)ta...
    叫我顏先生閱讀 253評(píng)論 0 0
  • Lua 5.1 參考手冊(cè) by Roberto Ierusalimschy, Luiz Henrique de F...
    蘇黎九歌閱讀 13,748評(píng)論 0 38
  • lua中每個(gè)值都可以擁有一個(gè)元表,元表是一個(gè)普通的lua table帅涂,定義了原始值在特定操作下的行為议薪。 setme...
    我和我的火柴閱讀 378評(píng)論 0 0
  • 前言 元表對(duì)應(yīng)的英文是metatable,元方法是metamethod媳友。我們都知道斯议,在C++中,兩個(gè)類是無(wú)法直接相...
    BobWong閱讀 1,035評(píng)論 0 9