cocos-lua的iskindof在多重继承时有一个BUG,如下:
local A=class("ClassA")
local B=class("ClassB",A)
local binst= B.new()
print( iskindof(binst,"ClassA") )
ClassB继承自ClassA,因此B的对象理应也是ClassA的对象。但是输出结果却是false. (如果你已经知道这个BUG,想知道怎么解决,直接跳到最后;如果你想看是什么原因,则往下看。)
iskindof源码
那么就要看下iskindof的代码了。源码如下:
function iskindof(obj, classname)
local t = type(obj)
if t ~= "table" and t ~= "userdata" then return false end
local mt
if t == "userdata" then
if tolua.iskindof(obj, classname) then return true end
mt = tolua.getpeer(obj)
else
mt = getmetatable(obj) --我传进来的binst是走到这个分支
end
if mt then
--最后调用这个,mt就是binst的metatable,classname就是传进来的"ClassA"
return iskindof_(mt, classname)
end
return false
end
略有点复杂,iskindof
流程大概是先判断传进来的obj什么类型,如果是table类型的则取它的元表metatable;如果是userdata类型的,则用tolua的方式来判断。
我们的传进去的是binst,是个table类型;所以执行getmetatable。然后再去调用iskindof_
。单纯按照kindof
这个函数的意图来讲,这个貌似没什么问题:“传进来如果是个table,那么我们就去查它的metatable是个什么类就可以了。”
那再看看最后一步iskindof_
是个什么函数,源码如下:
local iskindof_
iskindof_ = function(cls, name)
local __index = rawget(cls, "__index")
if type(__index) == "table" and rawget(__index, "__cname") == name then return true end
if rawget(cls, "__cname") == name then return true end
local __supers = rawget(cls, "__supers") --获取的__supers为空
if not __supers then return false end
for _, super in ipairs(__supers) do
if iskindof_(super, name) then return true end
end
return false
end
(程序中rawget意思是只找当前表里的内容,避免lua自动去找这个表的元表的内容)
其大致的流程是:
1.先找cls的__index,看看__index的类名是不是我们要的。
2.如果不是,看看cls本身是不是我们要的。
3.如果不是,那么我们看看cls的父类有没有我们要的。找父类的过程是个迭代的过程,只要找到了,则return true;没有找到,则继续找下一个父类。
按照这个函数的意思,如果当前类找不到类名,会去找父类。但是我运行了一下,当把binst的元表传进去之后,__supers为空!这显然是不对的,binst明明是有父类的,怎么会没有__supers呢?肯定哪里错误了。
所以接下来我们要解决的问题是,也是本文的中心:
为什么iskindof\_
找不到binst的__supers?
不过,我承认iskindof_
这个函数有点不好消化,因为一下子来了那么多新的域,什么__index、__supers、__cname等等。这就涉及到class的内部结构了。
再粘一下class的源码,不要被吓到。吓到了,直接跳过看我的研究成果吧。
class的结构
function class(classname, ...)
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",