lua中沒有類的概念,有的只是表(table),而類之間的繼承也就是將父類的表連到了一起,派生類中沒有找到的屬性和方法就通過元表查找父類,在cocos2d-lua中,封裝好的class方法,完美的實現了類的繼承,包括單繼承,和多繼承,class的源碼如下(省去了一些不必要的代碼):
1 _setmetatableindex = function(t, index) 2 if type(t) == "userdata" then 3 local peer = tolua.getpeer(t) 4 if not peer then 5 peer = {} 6 tolua.setpeer(t, peer) 7 end 8 _setmetatableindex(peer, index) 9 else 10 local mt = getmetatable(t) 11 if not mt then mt = {} end 12 if not mt.__index then 13 mt.__index = index 14 setmetatable(t, mt) 15 elseif mt.__index ~= index then 16 _setmetatableindex(mt, index) 17 end 18 end 19 end 20 21 22 function class(classname, ...) --參數一:所要創建的類名,參數二:可選參數,可以使function,也可以是table,userdata等 23 local cls = {__cname = classname} 24 25 local supers = {...} 26 for _, super in ipairs(supers) do --遍歷可選參數 27 local superType = type(super) 28 if superType == "function" then 29 --如果是個function,那么就讓cls的create方法指向他 30 cls.__create = super 31 elseif superType == "table" then --如果是個table 32 if super[".isclass"] then--如果是個原生cocos類,比如cc.Sprite,不是自定義的 33 cls.__create = function() return super:create() end 34 else 35 -- 如果是個純lua類,自己定義的那種,比如a={} 36 cls.__supers = cls.__supers or {} 37 cls.__supers[#cls.__supers + 1] = super--不斷加到__supers的數組中 38 if not cls.super then 39 -- 把第一個遍歷到的table作為cls的超類 40 cls.super = super 41 end 42 end 43 44 end 45 end 46 47 cls.__index = cls--不知道作用 48 if not cls.__supers or #cls.__supers == 1 then --這個就是單繼承,設置cls的元表的index為他的第一個超類 49 setmetatable(cls, {__index = cls.super}) 50 else 51 setmetatable(cls, {__index = function(_, key)--羡慕是多繼承,index指向一個函數,到時候找元素的時候會遍歷函數 52 local supers = cls.__supers 53 for i = 1, #supers do 54 local super = supers[i] 55 if super[key] then return super[key] end 56 end 57 end}) 58 end 59 60 if not cls.ctor then 61 -- 增加一個默認構造函數 62 cls.ctor = function() end 63 end 64 cls.new = function(...) --新建方法,這個也是比較重要的方法 65 local instance 66 if cls.__create then 67 --如果有create方法,那么就調用,正常情況下,自定義的cls是沒有create方法的。 68 --會不斷的向上尋找元類的index,直到找到原生cocos類,比如sprite,然后調用sprite:create() 69 --返回一個原生對象,通過調試代碼,可以得出這些 70 71 instance = cls.__create(...) 72 else 73 instance = {}--沒有,說明根目錄不是cocos類,而是普通類 74 end 75 --這個方法也比較關鍵,設置instance的元類index,誰調用new了,就把他設置為instance的元類index 76 --具體可以看代碼 77 _setmetatableindex(instance, cls) 78 instance.class = cls 79 instance:ctor(...)--調用構造函數 80 return instance 81 end 82 cls.create = function(_, ...) 83 return cls.new(...) 84 end 85 86 return cls 87 end
通過以上方法,可以實現類的繼承,那么如何調用這個方法呢,看下面的事例
為了說明事例,我又精簡了上面的代碼,便於我們說明
local _setmetatableindex _setmetatableindex2 = function(t, index) -- print("總是Sprite=" .. t.type ) local mt = getmetatable(t) if not mt then mt = {} print("第一個分支") end if not mt.__index then mt.__index = index setmetatable(t, mt) print("第二個分支") elseif mt.__index ~= index then print("第三個分支") _setmetatableindex2(mt, index) end end function class2(classname, ...) local cls = {__cname = classname} local supers = {...} for _, super in ipairs(supers) do local superType = type(super) if superType == "function" then cls.__create = super elseif superType == "table" then if super[".isclass"] then cls.__create = function() return super:create() end print("走上面") else print("走下面") cls.__supers = cls.__supers or {} cls.__supers[#cls.__supers + 1] = super if not cls.super then cls.super = super end end end end cls.__index = cls if not cls.__supers or #cls.__supers == 1 then setmetatable(cls, {__index = cls.super}) end cls.new = function(...) print("走一遍") local instance if cls.__create then instance = cls.__create(...) print("有函數") else instance = {} print("無函數") end _setmetatableindex2(instance, cls) instance.class = cls return instance end cls.create = function(_, ...) return cls.new(...) end return cls end
調用:
--第一個類,這里模仿cocos的Sptite類
local Sprite={} Sprite.type="Sprite" Sprite.wenqian1=111 Sprite[".isclass"]=true function Sprite:create(o) o=o or {} setmetatable(o,self) ; self.__index=self; return o end
--第二個類,這里他繼承了Sprite類 GameSprite = class2("GameSprite", -- function() return Sprite:create() end Sprite ) GameSprite.wenqian2=222 --第三個類,這里繼承了 GameSprite類 testClass=class2("testClass", -- function() -- return GameSprite:create() -- end GameSprite--,GameSprite2 ) testClass.wenqian3=333 --實例化一個testClass類的對象 local test=testClass:new()
print(test.wenqian1);
print(test.wenqian2)
print(test.wenqian3)
運行結果如下:
[LUA-print] 走上面
[LUA-print] 走下面
[LUA-print] 走一遍
[LUA-print] 有函數
[LUA-print] 第三個分支
[LUA-print] 第一個分支
[LUA-print] 第二個分支
[LUA-print] 111
[LUA-print] 222
[LUA-print] 333
通過class里面的代碼,可以看出 返回的local test 實際上就是最頂層的Sprite 類的一個新建對象,然后他的元類的index為testClass,
而testClass的元類的index為GameSprite,從而wenqian1,wenqian2,wenqian3都能夠正確的找到。
下面說一下錯誤的情況,之前由於學習lua比較倉促,沒有細看class的原理,所以用了下面的錯誤方法調用local Sprite={}
Sprite.type="Sprite" Sprite.wenqian1=111 Sprite[".isclass"]=true function Sprite:create(o) o=o or {} setmetatable(o,self) ; self.__index=self; return o end GameSprite = class2("GameSprite", -- function() return Sprite:create() end Sprite ) GameSprite.wenqian2=222 testClass=class2("testClass", function() return GameSprite:create() end ) testClass.wenqian3=333 local test=testClass:new() --print( getmetatable(test).__index==GameSprite) --print( getmetatable(getmetatable(test)).__index==testClass) print(test.wenqian1); print(test.wenqian2) print(test.wenqian3)
改動的地方就是紅色字體部分,以前第二個參數是GameSprite類,現在成了一個返回GameSprite:create方法,運行結果如下:
[LUA-print] 走上面
[LUA-print] 走一遍
[LUA-print] 走一遍
[LUA-print] 有函數
[LUA-print] 第三個分支
[LUA-print] 第一個分支
[LUA-print] 第二個分支
[LUA-print] 有函數
[LUA-print] 第三個分支
[LUA-print] 第三個分支
[LUA-print] 第一個分支
[LUA-print] 第二個分支
[LUA-print] 111
[LUA-print] 222
[LUA-print] nil
從結果可知,test 本身的屬性wenqian3沒有取出來,是空值,這是為什么呢,通過分析代碼可以知道,new的方法走了兩次,
使得Sprite的對象的元類的index成了GameSprite,而GameSprite的元表的元表成了testClass,而不是元表的index是testClass,也就是說
Sprite的對象無法找到wenqian3字段,那么他就會去他元表的index字段,
也就是GameSprite中去找,但是GameSprite依然無法提供wenqian3,所以GameSprite去他的元表的index中
找,但是通過上面的程序得知GameSprite的元表的index不是testClass,所以無法找到他
所以綜上所述得知,如果新建的類繼承的直接是cocos系列的類,那么可以用比如直接寫類名,或者返回create函數
而如果是繼承的自定義的類,那么最簡單,最直接的還是直接寫類名,而寫函數的話,如果處理不當可能會出現奇怪的錯誤,當然函數也是可以實現的,具體情況需要具體分析
