cocos2d-lua class 方法解释

lua中没有类的概念,有的只是表(table),而类之间的继承也就是将父类的表连到了一起,派生类中没有找到的属性和方法就通过元表查找父类,在cocos2d-lua中,封装好的class方法,完美的实现了类的继承,包括单继承,和多继承,class的源码如下(省去了一些不必要的代码):

_setmetatableindex = function(t, index)
    if type(t) == "userdata" then
        local peer = tolua.getpeer(t)
        if not peer then
            peer = {}
            tolua.setpeer(t, peer)
        end
        _setmetatableindex(peer, index)
    else
        local mt = getmetatable(t)
        if not mt then mt = {} end
        if not mt.__index then
            mt.__index = index
            setmetatable(t, mt)
        elseif mt.__index ~= index then
            _setmetatableindex(mt, index)
        end
    end
end


function class(classname, ...) --参数一:所要创建的类名,参数二:可选参数,可以使function,也可以是table,userdata等
    local cls = {__cname = classname}

    local supers = {...}
    for _, super in ipairs(supers) do --遍历可选参数
        local superType = type(super)
          if superType == "function" then
            --如果是个function,那么就让cls的create方法指向他
            cls.__create = super
        elseif superType == "table" then --如果是个table
            if super[".isclass"] then--如果是个原生cocos类,比如cc.Sprite,不是自定义的
                 cls.__create = function() return super:create() end
            else
                -- 如果是个纯lua类,自己定义的那种,比如a={}
                cls.__supers = cls.__supers or {}
                cls.__supers[#cls.__supers + 1] = super--不断加到__supers的数组中
                if not cls.super then
                    -- 把第一个遍历到的table作为cls的超类
                    cls.super = super
                end
            end
        
        end
    end

    cls.__index = cls--不知道作用
    if not cls.__supers or #cls.__supers == 1 then --这个就是单继承,设置cls的元表的index为他的第一个超类
        setmetatable(cls, {__index = cls.super})
    else
        setmetatable(cls, {__index = function(_, key)--羡慕是多继承,index指向一个函数,到时候找元素的时候会遍历函数
            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
        -- 增加一个默认构造函数
        cls.ctor = function() end
    end
    cls.new = function(...) --新建方法,这个也是比较重要的方法
        local instance
        if cls.__create then 
            --如果有create方法,那么就调用,正常情况下,自定义的cls是没有create方法的。
            --会不断的向上寻找元类的index,直到找到原生cocos类,比如sprite,然后调用sprite:create()
            --返回一个原生对象,通过调试代码,可以得出这些
            
            instance = cls.__create(...)
        else
            instance = {}--没有,说明根目录不是cocos类,而是普通类
        end
        --这个方法也比较关键,设置instance的元类index,谁调用new了,就把他设置为instance的元类index
        --具体可以看代码
        _setmetatableindex(instance, cls)
        instance.class = cls
        instance:ctor(...)--调用构造函数
        return instance
    end
    cls.create = function(_, ...)
        return cls.new(...)
    end

    return cls
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函数

而如果是继承的自定义的类,那么最简单,最直接的还是直接写类名,而写函数的话,如果处理不当可能会出现奇怪的错误,当然函数也是可以实现的,具体情况需要具体分析


转载地址  http://www.cnblogs.com/xiaonanxia/p/4971356.html
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值