// 读取ccb文件
self.layer = CCBuilderReaderLoad("jiLuLayer.ccbi",CCBProxy:create(),self)
// 强转类型
self.layer = tolua.cast(self.layer,"CCLayer")
__call元方法释义等价于 #ttable.getn(t)但是它计算的是数组元素。不包括hash 键值。而且数组是以第一个nil元素来判断数组结束。#只计算array的元素个数,它实际上调用了对象的metatable 的__len函数。对于有__len 方法的函数返回函数返回值。不然就返回数组成员数目。==2== a={1,3,a='b',[6]='six',['10']='ten'}a 和 [6] ['10']是作为hash保存的。#a => 2 他是不包括hash成员的计数。 1 3 是 数组结构保存的。table.maxn(a) => 6 因为a中所有元素最大的数值索引是6不是字符串10
当我们写下a(b,c )时,若a不是函数,那么a.metatable.__call(a, ...)将调用:
meta = {}
meta.__call = function(a, b, c) print(a, b, c) end
a = {}
setmetatable(a, meta)
a(10, 11)
__gc元方法释义
注意__gc元方法只对full user data有效
__tostring元方法释义
当调用tostring(a)时,若a不是string,那么a.metatable.__tostring(a)将调用;事实上print会调用tostring
meta = {}
meta.__tostring = function(a) return "a" end -- 必须返回一个字符串
a = {}
setmetatable(a, meta)
print(a)
禁止目标的metatable被修改
setmetatable/getmetatable函数也会使用metafield,在这种情况下,可以保护metatables。假定你想保护你的集合使其使用者既看不到也不能修改metatables。如果你对metatable设置了__metatable的值,getmetatable将返回这个域的值,而调用setmetatable 将会出错:
meta = { __metatable = "not your business" }
a = {}
setmetatable(a, meta) -- ok & metatable protected
print(getmetatable(a)) -- not your business
setmetatable(a, {}) -- error!!!
lua元表
本文简译自一篇老外的博客,写得不错可惜我翻译的太烂,简译如下。
(key--value常见翻译为“键值对”,我翻译为索引、值)
在这篇教程里我会介绍Lua中一个重要的概念: metatable(元表),掌握元表可以让你更有效的
使用Lua。 每一个tabel都可以附加元表, 元表是带有索引集合的表,它可以改变被附加表的行为。
看下例:
t = {} -- 普通表
mt = {} -- 元表,现在暂时什么也没有
setmetatable(t, mt) -- 把mt设为t的元表
getmetatable(t) -- 这回返回mt
如你所见 getmetatable
和setmetatable
是主要的函数。 当然我们可以把上面的三行代码合为:
t = setmetatable({}, {})
setmetatable
返回第一个参数, 因此我们可以使用这个简短的表达式。现在,我们在元表里放些什
么呢? 元表可以包含任何东西,但是元表通常以"__"(两个下划线)开头的索引(当然string类型)
来调用,例如__index和__newindex。 和索引对应的值可以是表或者函数,例如:
t = setmetatable({}, {
__index = function(t, key)
if key == "foo" then
return 0
else
return table[key]
end
end
})
我们给__index索引分配了一个函数,
让我们来看看这个索引是干啥的。
__index
元表里最常用的索引可能是__index,它可以包含表或函数。
当你通过索引来访问表, 不管它是什么(例如t[4]
, t.foo
, 和t["foo"]
), 以及并没有分配索引的值时,
Lua 会先在查找已有的索引,接着查找表的metatable里(如果它有)查找__index
索引。 如果
__index
包含了表, Lua会在__index包含的表里查找索引。
这听起来很迷糊,让我们看一个例子。
other = { foo = 3 }
t = setmetatable({}, { __index = other })
t.foo -- 3 ,现在__index包含的表{foo=3}查找
t.bar -- nil ,没找到
如果__index
包含一个函数,当被它调用时,会把被访问的表和索引作为参数传入。从上面的例子来看,
我们可以使用带有条件语句的索引,以及任意的Lua语句。因此在这种情况下,如果索引和字符串"foo"
相等,我们可以返回0,否则,我们可以查询表中被使用的索引;当"foo"被使用时,
让t
作为table
的
别名并返回0。(这句不是太懂,原文为:Therefore,in that example, if the key was equal to
the string "foo" we would return 0, otherwise we look up the table
table with the key that
was used; this makes t
an alias of table
that returns 0 when the key "foo" is used.)
你可能会疑问,怎么把表作为是第一个传给__index
函数的参数。当你在多个表里使用相同的元表时,
这会很方便,并支持代码复用和节省电脑资源。我们会在最下面的Vector
类里看到解释。
--注:下面是我的一个例子
other = function(t,k) if k=="foo" then return 0 end end
t = setmetatable({}, { __index = other })
print(t.foo)
__newindex
下一个是__newindex
, 它和__index类似。
和 __index一样,它可以包含函数和表。当你给表中不存在
的值赋值时,
Lua会在metatable里查找__newindex,调用顺序和
__index
一样。如果__newindex
是表,
索引和值会设置到指定的表:
other = {}
t = setmetatable({}, { __newindex = other })
t.foo = 3 --t里没有foo,查看__newindex,并把foo=3传给了other,并没有给t里的foo赋值
other.foo – 3 故为3
t.foo – nil 故为 nil
和期望的一样,__newindex
是函数时,当被调用时会传递表、索引、值三个参数。
t = setmetatable({}, {
__newindex = function(t, key, value)
if type(value) == "number" then
rawset(t, key, value * value)
else
rawset(t, key, value)
end
end
})
t.foo = "foo"
t.bar = 4
t.la = 10
t.foo -- "foo"
t.bar -- 16
t.la -- 100
当在t里创建新的索引时
,如果值是number,这个值会平方,否则什么也不做。下面介绍rawget
和rawset。
rawget
和 rawset
有时需要get 和set表的索引,不想使用metatable.你可能回猜想, rawget
允许你得到索引无需__index
,
rawset
允许你设置索引的值无需__newindex
(不,相对传统元表的方式,这些不会提高速度)。为了避免陷
在无限循环里,你才需要使用它们。 在上面的例子里, t[key] = value * value将再次调用
__newindex
函数,
这让你的代码陷入死循环。使用rawset(t, key, value * value)
可以避免。
你可能看到,使用这些函数, 我们必须传递参数目标table, key, 当你使用rawset时还有value。
操作符
许多元表的索引是操作符 (如, +
, -
, 等),允许你使用表完成一些操作符运算。例如,我们想要一个表支持
乘法操作符(*
), 我们可以这样做:
t = setmetatable({ 1, 2, 3 }, {
__mul = function(t, other) ,
new = {}
for i = 1, other do
for _, v in ipairs(t) do table.insert(new, v) end
end
return new
end
})
t = t * 2 -- { 1, 2, 3, 1, 2, 3 }
这允许我们创建一个
使用乘法操作符重复某些次数的新表。你也看的出来, __mul
和乘法相当的索引是,
与__index、
__newindex
不同,操作符索引只能是函数。 它们接受的第一个参数总是目标表, 接着
是右值 (除了一元操作符“-”,即索引
__unm
)。下面是操作符列表:
__add
: 加法(+
)__sub
: 减法(-
)__mul
: 乘法(*
)__div
: 除法(/
)__mod
: 取模(%
)__unm
: 取反(-)
, 一元操作符__concat
: 连接(..
)__eq
: 等于(==
)__lt
: 小于(<
)__le
:小于等于(<=
)
(只有==
, <
, <=
,因为你能通过上面的实现所有操作,事实上==
和<就足够了
)
__call
接下来是__call
索引, 它允许你把表当函数调用,代码示例:
t = setmetatable({}, {
__call = function(t, a, b, c, whatever)
return (a + b + c) * whatever
end
})
t(1, 2, 3, 4) –- 24 ,表t在调用时先查找__call,调用里面的函数,t便相当于函数了
和通常一样在call里的函数,被传递了一个目标表,还有一些参数。__call
非常有用,经常用来在表和它
里面的函数之间转发调用(原文it's used for is forwarding a call on a table to a function inside
that table.)。 kikito的 tween.lua 库就是个例子tween.start
可以被自身调用(tween
). 另一个例子是
MiddleClass, 类里的new函数可以被类自身调用。
__tostring
最后一个是 __tostring。
如果实现它,那么tostring
可以把表转化为string, 非常方便类似print的函数
使用。 一般情况下,当你把表转为string时, 你需要"table: 0x<hex-code-here",但是你可以仅用
__tostring来解决
。示例:
t = setmetatable({ 1, 2, 3 }, {
__tostring = function(t)
sum = 0
for _, v in pairs(t) do sum = sum + v end
return "Sum: " .. sum
end
})
print(t) -- prints out "Sum: 6"
创建一个向量类
下面我们来封装一个2D 向量类(感谢 hump.vector 的大量代码)。代码太长你可以查看gist #1055480,
代码里有大量的metatable概念,(注意,如果你之前没接触面向对象可能会有点难)。
Vector = {}
Vector.__index = Vector
首先声明了一个Vector
class, 设置了__index
索引指向自身。 这在干啥呢?你会发现我们把所有的元表
放到Vector类里了。
你将看到在Lua里实现OOP (Object-Oriented Programming)的最简单方式。Vector
表代表类
, 它包含了所有方法,类的实例可以通过Vector.new
(如下) 创建了。
function Vector.new(x, y)
return setmetatable({ x = x or 0, y = y or 0 }, Vector)
end
它创建了一个新的
, 然后把metatable设置到带有x、
y
属性的表Vector
类。我们知道Vector
包含了所有的
元方法,特别是 __index。这意味着我们通过新表可以使用所有
Vector
里方法。
另外重要的一行是:
setmetatable(Vector, { __call = function(_, ...) return Vector.new(...) end })
这意味着我们可以创建一个新的Vector
实例通过 Vector.new或者仅
Vector。
最后重要的事,你可能没注意冒号语法。当我们定义一个带有冒号的函数时,如下:
function t:method(a, b, c)
-- ...
end
我们真正定义的是这个函数:
function t.method(self, a, b, c)
-- ...
end
这是一个语法糖,帮助我们使用OOP。当调用函数时,我们可以这样使用冒号语法:
-- these are the same
t:method(1, 2, 3)
t.method(t, 1, 2, 3)
我们如何使用 Vector
类? 示例如下:
a = Vector.new(10, 10)
b = Vector(20, 11)
c = a + b
print(a:len()) -- 14.142135623731
print(a) -- (10, 10)
print(c) -- (30, 21)
print(a < c) -- true
print(a == b) -- false
因为Vector
里有__index,我们可以在实例里使用它的所有方法。
require "SpriteTest"
MainLayer=class("MainLayer",
function()
-- 返回ccb对象
local layer = CCBuilderReaderLoad("MainScene.ccbi",CCBProxy:create(),MainLayer)
layer = tolua.cast(layer,"CCLayer")
return layer;
end
)
-- 保存当前对象
local this = null;
-- 创建layer
function MainLayer:createLayer()
this = self:new();
-- 创建一个精灵
local sp = SpriteTest.createSp();
this:addChild(sp);
sp:setPosition(ccp(100,100));
return this;
end
-- 创建时候调用的方法 相当于init
function MainLayer:ctor()
local function sceneEventHandler( eventType )
-- print(eventType)
if eventType == kCCNodeOnEnter then
self:onEnter()
else
self:onExit()
end
end
self:registerScriptHandler(sceneEventHandler)
end
function MainLayer:onEnter()
print("进入enter");
end
function MainLayer:onExit()
print("进入exit");
end
-- 点击button事件
function MainLayer:onPressButton()
cclog("dddd");
-- 执行ccb动画
self.mAnimationManager:runAnimationsForSequenceNamed("ddd");
--local scenen = CCScene:create();
--CCDirector:sharedDirector():runWithScene(scenen);
end
-- 创建场景
MainLayer.createScene = function()
local scene = CCScene:create();
scene:addChild(MainLayer:createLayer());
return scene;
end
table.foreachi(table, function(i, v)) 会期望一个从 1(数字 1)开始的连续整数范围,遍历table中的key和value逐对进行function(i, v)操作 t1 = {2, 4, 6, language="Lua", version="5", 8, 10, 12, web="hello lua"}; table.foreachi(t1, function(i, v) print (i, v) end) ; --等价于foreachi(t1, print) 输出结果: 1 2 2 4 3 6 4 8 5 10 6 12 table.foreach(table, function(i, v)) 与foreachi不同的是,foreach会对整个表进行迭代 t1 = {2, 4, 6, language="Lua", version="5", 8, 10, 12, web="hello lua"}; table.foreach(t1, function(i, v) print (i, v) end) ; 输出结果: 1 2 2 4 3 6 4 8 5 10 6 12 web hello lua language Lua version 5