• 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函数

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

  • 相关阅读:
    atom 安装插件列表
    django学习
    windows 安装 python3
    python3 监控代码变化 自动重启 提高开发效率
    git无法pull仓库refusing to merge unrelated histories
    python 项目部署virtualenv
    python 多线程并发threading & 任务队列Queue
    python logging 日志使用
    jupyter 教程
    mysql 替换数据库字段内容
  • 原文地址:https://www.cnblogs.com/xiaonanxia/p/4971356.html
Copyright © 2020-2023  润新知