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,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 欢迎去看原博客。