cocos2d-lua class 方法解釋


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函數

而如果是繼承的自定義的類,那么最簡單,最直接的還是直接寫類名,而寫函數的話,如果處理不當可能會出現奇怪的錯誤,當然函數也是可以實現的,具體情況需要具體分析

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM