lua -- class

Lua中没有类的概念,有的只有table表,而面向对象的实现只不过是将表与父类的表连在一起,没有某个变量的时候就去父类查找。cocos2dx-lua中有一个class函数实现了类的继承,包括了单继承和多重继承。

function class(classname, ...)     -- 参数一:要创建的类名称,参数二:父类-可选参数,可以是table,function,userdata等
    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));
            -- 如果父类是个function的话,就让cls的create方法指向他
            cls.__create = super
        elseif superType == "table" then  -- 如果父类是table
            if super[".isclass"] then
                -- 如果父类是原生的cocos类,比如cc.Node,不是自己定义的类
                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
                -- 如果父类是自定义的类,比如 A = {}
                cls.__supers = cls.__supers or {}
                cls.__supers[#cls.__supers + 1] = super       -- 保存cls的多个父类的table表
                if not cls.super then
                    -- 将遍历到的第一个table作为cls的父类
                    cls.super = super
                end
            end
        else
            -- 如果父类既不是table,也不是function的话,报错。如果父类不存在,不会执行这个循环的,所以super可以为nil。
            error(string.format("class() - create class \"%s\" with invalid super type",
                        classname), 0)
        end
    end
    -- 前面一大段是找到cls的所有父类;
    -- 接下来就是设置cls的元表了。
    -- 设置cls的第一索引对象是自己,如果实例对象找不到某个参数的话,
    --就会查找该类是否包含该参数,如果该类也不包含的话,就去父类查找
    cls.__index = cls   
    if not cls.__supers or #cls.__supers == 1 then  
        -- 如果cls只有一个父类,即单继承的话,设置cls的父类是他的元表
        setmetatable(cls, {__index = cls.super})
    else
        -- 如果cls是多重继承的话,__index为一个函数,找元素的时候就会执行一遍该函数
        setmetatable(cls, {__index = function(_, key)
            -- 遍历所有父类,查找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
        -- 增加一个默认的构造函数
        cls.ctor = function() end
    end
    cls.new = function(...)  -- 新建方法,这个也是比较重要的方法
        local instance
        if cls.__create then
            -- 如果有create方法,那么就调用,正常情况下,自定义的cls是没有create方法的。
            -- 通过__index和元表的index,一级一级往上找,直到找到原生cocos类
            instance = cls.__create(...)
        else
            instance = {}  -- 没有create方法,他是一个普通类
        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

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

通过上面的class我们可以实现类的继承,如何调用呢,看下面的例子:
为了说明事例,将上面代码改造一下。

setmetatableindex = function(t, index)

    local mt = getmetatable(t)
    if not mt then
        mt = {} 
        print("setmetatableindex0001")

    end
    if not mt.__index then
        mt.__index = index
        setmetatable(t, mt)
        print("setmetatableindex0002")

    elseif mt.__index ~= index then
        print("setmetatableindex0003")
        setmetatableindex(mt, index)
    end
end

function class(classname, ...)
    local cls = {__cname = classname}

    local supers = {...}   
    for _, super in ipairs(supers) do 
        local superType = type(super)

        if superType == "function" then
            print("super type is function")
            cls.__create = super
        elseif superType == "table" then
            if super[".isclass"] then
                print("super is native class")

                cls.__create = function() return super:create() end
            else
               print("super is pure lua class")

                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  
        print("single inherit")
        setmetatable(cls, {__index = cls.super})
    else
        print("multi inherit")
        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
        cls.ctor = function() end
    end
    cls.new = function(...) 

        print("走一遍")
        local instance
        if cls.__create then
            print("had create func")
            instance = cls.__create(...)
        else
            print("no create func")
            instance = {}  
        end
        setmetatableindex(instance, cls)
        instance.class = cls
        instance:ctor(...) 
        return instance
    end
    cls.create = function(_, ...)
        return cls.new(...)
    end

    return cls
end

调用:写一个Sprite.lua类

require("class")
-- 第一个类,模仿cocos的Sprite类

local Sprite = {}

Sprite.type = "Sprite"
Sprite.mark1 = 111
Sprite[".isclass"] = true
function Sprite:create( o )
    o = o or {}
    setmetatable(o,self)
    self.__index = self
    return o
end

-- 第二个类,这里他继承了Sprite类

GameSprite = class("GameSprite",
    -- function ()
    --  return Sprite:create()
    -- end
    Sprite
)
GameSprite.type = "GameSprite"
GameSprite.mark2 = 222

-- 第三个类,这里继承GameSprite类
testClass = class("testClass",

    -- function()
    --  local s = GameSprite.create()
    --  print("s.type:" .. s.type)
    --  return s
    -- end
    GameSprite
)
testClass.type = "testClass"
testClass.mark3 = 333

-- 实例化一个testclass类的对象
local test = testClass:new()

print(test.mark1)
print(test.mark2)
print(test.mark3)

输出:

super is native class
single inherit
super is pure lua class
single inherit
走一遍
had create func
setmetatableindex0003
setmetatableindex0001
setmetatableindex0002
111
222
333

通过class代码,可以看到local test 返回的实际是最顶层Sprite类的一个新建对象,然后他的元表是testClass,而testClass的元表的index是GameSprite,从而可以找到mark1,mark2,mark3都能够正确找到。

下面说明一个错误:
将上面的Sprite.lua文件的第三个类testClass的继承使用如下方法:

···
testClass = class("testClass",

    function()
        local s = GameSprite.create()
        print("s.type:" .. s.type)
        return s
    end
    -- GameSprite
)
···

再次运行,输出结果是:

super is native class
single inherit
super type is function
single inherit
走一遍
had create func
走一遍
had create func
setmetatableindex0003
setmetatableindex0001
setmetatableindex0002
s.type:Sprite
setmetatableindex0003
setmetatableindex0003
setmetatableindex0001
setmetatableindex0002
111
222
nil

从结果可以知道,test本身的属性mark3并没有取出来,是一个空值,Why! ! !
输出显示,我们调用了两次new方法,结果变成了:Sprite的对象的元表的index成了
GameSprite,而GameSprite的元表的元表成了testClass,而不是元表的index是testClass,也就是说Sprite对象无法找到mark3,然后去GameClass中查找,也没有查找到,又去GameClass的元表的index中茶盅,但是GameSprite的元表的index不是testClass,所以无法找到。
所以使用官方的class注意一下问题:
如果新建的类是直接继承cocos系列的类,可以直接写类名或者是使用返回create函数的方法;
但是继承的是自定义的类,那么还是使用直接谢类名,如果写函数的话,可能出现奇怪的错误。

以下是修改:
将class中的setmetatableindex函数修改成以下:

setmetatableindex = function(t, index)
    local mt = getmetatable(t)
    if not mt then
        mt = {} 
        print("setmetatableindex0001")
    end
    if not mt.__index then
        mt.__index = index
        setmetatable(t, mt)
        print("setmetatableindex0002")
    elseif mt.__index ~= index then
        print("setmetatableindex0003")
        -- 修改这个地方,将mt修改成mt.__index
        setmetatableindex(mt.__index, index)
    end
end

就能解决上面产生的问题。不行试一下咯。

由于自己是cocos2dx新手,所以该博客内容也是来自于http://www.cnblogs.com/xiaonanxia/p/4971356.html
欢迎去看原博客。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值