打开Xcode6,new Project的时候Language选择Swift
就可以看到很简单的一段
1 2 3 |
import Foundation println("Hello, World!") |
就这么简单
打开Xcode6,new Project的时候Language选择Swift
就可以看到很简单的一段
1 2 3 |
import Foundation println("Hello, World!") |
就这么简单
转自http://www.swfdiy.com/?p=1663
在quick-cocos2dx里, 通过class() 函数进行类的定义
下面贴出quick-cocos2d-x-2.2.1-rc 里 class()的源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
function class(classname, super) local superType = type(super) local cls if superType ~= "function" and superType ~= "table" then superType = nil super = nil end if superType == "function" or (super and super.__ctype == 1) then -- inherited from native C++ Object cls = {} if superType == "table" then -- copy fields from super for k,v in pairs(super) do cls[k] = v end cls.__create = super.__create cls.super = super else cls.__create = super cls.ctor = function() end end cls.__cname = classname cls.__ctype = 1 function cls.new(...) local instance = cls.__create(...) -- copy fields from class to native object for k,v in pairs(cls) do instance[k] = v end instance.class = cls instance:ctor(...) return instance end else -- inherited from Lua Object if super then cls = {} setmetatable(cls, {__index = super}) cls.super = super else cls = {ctor = function() end} end cls.__cname = classname cls.__ctype = 2 -- lua cls.__index = cls function cls.new(...) local instance = setmetatable({}, cls) instance.class = cls instance:ctor(...) return instance end end return cls end |
我们先考虑最简单的情况, 在没有继承的情况下定义一个类ClassA
1 2 3 4 5 6 7 |
--ClassA.lua local ClassA = class("ClassA") ClassA.field1 = "this is field1" return ClassA --main.lua local ClassA = require("ClassA") local InstanceA = ClassA.new() |
这种情况下, ClassA 跟InstanceA的关系如下图
InstanceA的metatable为ClassA, 而且ClassA.__index = ClassA, 因此,对于InstanceA找不到的属性, 将在ClassA里进行查找。 需要注意的是,ClassA里的属性比如field1, 是相当于类变量的概念,所有实例都公用该属性,并非各自维护该字段的拷贝。
下面如果要再定义ClassB, 从ClassA 进行继承
1 2 3 4 |
--ClassB.lua local ClassB = class("ClassB", require("ClassA")) return ClassB |
这种情况下, ClassB 跟InstanceB, ClassA的关系如下图
同理,InstanceB里没有定义的属性, 将首先在ClassB里进行搜索, 如果ClassB里搜不到, 将向上追寻到ClassA里。
上面考虑的都只是针对纯Lua类, 在实际情况中, 我们需要对C++里生成的对象(userdata)进行扩展, 可以如下定义
1 2 3 4 5 6 7 8 9 10 |
--ClassC.lua local ClassC = class("ClassC", function() --调用C++接口创建一个原生对象(userdata), 并且给该对象绑定一个peer(table), 这里我们以创建一个CCNode为例 local node = CCNode:create() --原生对象(userdata) local peer = {} tolua.setpeer(node , peer ) return node end) return ClassC |
ClassC跟InstanceC的关系如下图
在调用InstanceC(实际上是一个userdata)上的某个方法时, 首先会先去这个userdata绑定的peer这个table里寻找这个方法,如果找不到才会去c++那层调用。
所以我们可以通过给peer添加几个方法从而实现给InstanceC(userdata)扩展方法, 实际情况上也确实如此,在quick里,有几个专门的类,CCNodeExtend,CCLayerExtend,CCSceneExtend,CCSpriteExtend, 使用比如 CCNodeExtend.extend(someCCNodeInstance), 可以生成一个特殊的peer, 这个peer的metatable为CCNodeExtend, 这样我们就可以在someCCNodeInstance上调用CCNodeExtend定义了而原本CCNode没有的方法, 从而实现扩展。
下面再考虑ClassD从ClassC继承的情况
1 2 3 4 5 |
--ClassD.lua local ClassD = class("ClassD", require("ClassC")) return ClassD |
关系图:
===================
最先是看到下面这篇文章的,也写的很棒
相关文章:http://childhood.logdown.com/posts/169509/detailed-quickcocos2dx-inheritance-mechanism
转自:http://wuzhiwei.net/lua_performance/
Lua是一门以其性能著称的脚本语言,被广泛应用在很多方面,尤其是游戏。像《魔兽世界》的插件,手机游戏《大掌门》《神曲》《迷失之地》等都是用Lua来写的逻辑。
所以大部分时候我们不需要去考虑性能问题。Knuth有句名言:“过早优化是万恶之源”。其意思就是过早优化是不必要的,会浪费大量时间,而且容易导致代码混乱。
所以一个好的程序员在考虑优化性能前必须问自己两个问题:“我的程序真的需要优化吗?”。如果答案为是,那么再问自己:“优化哪个部分?”。
我们不能靠臆想和凭空猜测来决定优化哪个部分,代码的运行效率必须是可测量的。我们需要借助于分析器来测定性能的瓶颈,然后着手优化。优化后,我们仍然要借助于分析器来测量所做的优化是否真的有效。
我认为最好的方式是在首次编写的时候按照最佳实践去写出高性能的代码,而不是编写了一堆垃圾代码后,再考虑优化。相信工作后大家都会对事后的优化的繁琐都深有体会。
一旦你决定编写高性能的Lua代码,下文将会指出在Lua中哪些代码是可以优化的,哪些代码会是运行缓慢的,然后怎么去优化它们。
在代码运行前,Lua会把源码预编译成一种中间码,类似于Java的虚拟机。这种格式然后会通过C的解释器进行解释,整个过程其实就是通过一个while
循环,里面有很多的switch...case
语句,一个case
对应一条指令来解析。
自Lua 5.0之后,Lua采用了一种类似于寄存器的虚拟机模式。Lua用栈来储存其寄存器。每一个活动的函数,Lua都会其分配一个栈,这个栈用来储存函数里的活动记录。每一个函数的栈都可以储存至多250个寄存器,因为栈的长度是用8个比特表示的。
有了这么多的寄存器,Lua的预编译器能把所有的local变量储存在其中。这就使得Lua在获取local变量时其效率十分的高。
举个栗子: 假设a和b为local变量,a = a + b
的预编译会产生一条指令:
1 2 |
;a是寄存器0 b是寄存器1 ADD 0 0 1 |
但是若a和b都没有声明为local变量,则预编译会产生如下指令:
1 2 3 4 |
GETGLOBAL 0 0 ;get a GETGLOBAL 1 1 ;get b ADD 0 0 1 ;do add SETGLOBAL 0 0 ;set a |
所以你懂的:在写Lua代码时,你应该尽量使用local变量。
以下是几个对比测试,你可以复制代码到你的编辑器中,进行测试。
1 2 3 4 5 6 |
a = os.clock() for i = 1,10000000 do local x = math.sin(i) end b = os.clock() print(b-a) -- 1.113454 |
把math.sin
赋给local变量sin
:
1 2 3 4 5 6 7 |
a = os.clock() local sin = math.sin for i = 1,10000000 do local x = sin(i) end b = os.clock() print(b-a) --0.75951 |
直接使用math.sin
,耗时1.11秒;使用local变量sin
来保存math.sin
,耗时0.76秒。可以获得30%的效率提升!
表在Lua中使用十分频繁,因为表几乎代替了Lua的所有容器。所以快速了解一下Lua底层是如何实现表,对我们编写Lua代码是有好处的。
Lua的表分为两个部分:数组(array)部分和哈希(hash)部分。数组部分包含所有从1到n的整数键,其他的所有键都储存在哈希部分中。
哈希部分其实就是一个哈希表,哈希表本质是一个数组,它利用哈希算法将键转化为数组下标,若下标有冲突(即同一个下标对应了两个不同的键),则它会将冲突的下标上创建一个链表,将不同的键串在这个链表上,这种解决冲突的方法叫做:链地址法。
当我们把一个新键值赋给表时,若数组和哈希表已经满了,则会触发一个再哈希(rehash)。再哈希的代价是高昂的。首先会在内存中分配一个新的长度的数组,然后将所有记录再全部哈希一遍,将原来的记录转移到新数组中。新哈希表的长度是最接近于所有元素数目的2的乘方。
当创建一个空表时,数组和哈希部分的长度都将初始化为0,即不会为它们初始化任何数组。让我们来看下执行下面这段代码时在Lua中发生了什么:
1 2 3 4 |
local a = {} for i=1,3 do a[i] = true end |
最开始,Lua创建了一个空表a,在第一次迭代中,a[1] = true
触发了一次rehash,Lua将数组部分的长度设置为2^0
,即1,哈希部分仍为空。在第二次迭代中,a[2] = true
再次触发了rehash,将数组部分长度设为2^1
,即2。最后一次迭代,又触发了一次rehash,将数组部分长度设为2^2
,即4。
下面这段代码:
1 2 |
a = {} a.x = 1; a.y = 2; a.z = 3 |
与上一段代码类似,只是其触发了三次表中哈希部分的rehash而已。
只有三个元素的表,会执行三次rehash;然而有一百万个元素的表仅仅只会执行20次rehash而已,因为2^20 = 1048576 > 1000000
。但是,如果你创建了非常多的长度很小的表(比如坐标点:point = {x=0,y=0}
),这可能会造成巨大的影响。
如果你有很多非常多的很小的表需要创建时,你可以将其预先填充以避免rehash。比如:{true,true,true}
,Lua知道这个表有三个元素,所以Lua直接创建了三个元素长度的数组。类似的,{x=1, y=2, z=3}
,Lua会在其哈希部分中创建长度为4的数组。
以下代码执行时间为1.53秒:
1 2 3 4 5 6 7 |
a = os.clock() for i = 1,2000000 do local a = {} a[1] = 1; a[2] = 2; a[3] = 3 end b = os.clock() print(b-a) --1.528293 |
如果我们在创建表的时候就填充好它的大小,则只需要0.75秒,一倍的效率提升!
1 2 3 4 5 6 7 |
a = os.clock() for i = 1,2000000 do local a = {1,1,1} a[1] = 1; a[2] = 2; a[3] = 3 end b = os.clock() print(b-a) --0.746453 |
所以,当需要创建非常多的小size的表时,应预先填充好表的大小。
与其他主流脚本语言不同的是,Lua在实现字符串类型有两方面不同。
第一,所有的字符串在Lua中都只储存一份拷贝。当新字符串出现时,Lua检查是否有其相同的拷贝,若没有则创建它,否则,指向这个拷贝。这可以使得字符串比较和表索引变得相当的快,因为比较字符串只需要检查引用是否一致即可;但是这也降低了创建字符串时的效率,因为Lua需要去查找比较一遍。
第二,所有的字符串变量,只保存字符串引用,而不保存它的buffer。这使得字符串的赋值变得十分高效。例如在Perl中,$x = $y
,会将$y的buffer整个的复制到$x的buffer中,当字符串很长时,这个操作的代价将十分昂贵。而在Lua,同样的赋值,只复制引用,十分的高效。
但是只保存引用会降低在字符串连接时的速度。在Perl中,$s = $s . 'x'
和$s .= 'x'
的效率差距惊人。前者,将会获取整个$s的拷贝,并将’x’添加到它的末尾;而后者,将直接将’x’插入到$x的buffer末尾。
由于后者不需要进行拷贝,所以其效率和$s的长度无关,因为十分高效。
在Lua中,并不支持第二种更快的操作。以下代码将花费6.65秒:
1 2 3 4 5 6 7 |
a = os.clock() local s = '' for i = 1,300000 do s = s .. 'a' end b = os.clock() print(b-a) --6.649481 |
我们可以用table来模拟buffer,下面的代码只需花费0.72秒,9倍多的效率提升:
1 2 3 4 5 6 7 8 9 |
a = os.clock() local s = '' local t = {} for i = 1,300000 do t[#t + 1] = 'a' end s = table.concat( t, '') b = os.clock() print(b-a) --0.07178 |
..
。应用table来模拟buffer,然后concat得到最终字符串。3R原则(the rules of 3R)是:减量化(reducing),再利用(reusing)和再循环(recycling)三种原则的简称。
3R原则本是循环经济和环保的原则,但是其同样适用于Lua。
有许多办法能够避免创建新对象和节约内存。例如:如果你的程序中使用了太多的表,你可以考虑换一种数据结构来表示。
举个栗子。 假设你的程序中有多边形这个类型,你用一个表来储存多边形的顶点:
1 2 3 4 5 6 |
polyline = { { x = 1.1, y = 2.9 }, { x = 1.1, y = 3.7 }, { x = 4.6, y = 5.2 }, ... } |
1 2 3 4 5 6 |
polyline = { { 1.1, 2.9 }, { 1.1, 3.7 }, { 4.6, 5.2 }, ... } |
一百万个顶点时,内存将会由153.3MB减少到107.6MB,但是代价是代码的可读性降低了。
最变态的方法是:
1 2 3 4 |
polyline = { x = {1.1, 1.1, 4.6, ...}, y = {2.9, 3.7, 5.2, ...} } |
一百万个顶点,内存将只占用32MB,相当于原来的1/5。你需要在性能和代码可读性之间做出取舍。
在循环中,我们更需要注意实例的创建。
1 2 3 4 5 |
for i=1,n do local t = {1,2,3,'hi'} --执行逻辑,但t不更改 ... end |
1 2 3 4 5 |
local t = {1,2,3,'hi'} for i=1,n do --执行逻辑,但t不更改 ... end |
如果无法避免创建新对象,我们需要考虑重用旧对象。
考虑下面这段代码:
1 2 3 4 |
local t = {} for i = 1970, 2000 do t[i] = os.time({year = i, month = 6, day = 14}) end |
{year = i, month = 6, day = 14}
,但是只有year
是变量。
下面这段代码重用了表:
1 2 3 4 5 6 |
local t = {} local aux = {year = nil, month = 6, day = 14} for i = 1970, 2000 do aux.year = i; t[i] = os.time(aux) end |
另一种方式的重用,则是在于缓存之前计算的内容,以避免后续的重复计算。后续遇到相同的情况时,则可以直接查表取出。这种方式实际就是动态规划效率高的原因所在,其本质是用空间换时间。
Lua自带垃圾回收器,所以我们一般不需要考虑垃圾回收的问题。
了解Lua的垃圾回收能使得我们编程的自由度更大。
Lua的垃圾回收器是一个增量运行的机制。即回收分成许多小步骤(增量的)来进行。
频繁的垃圾回收可能会降低程序的运行效率。
我们可以通过Lua的collectgarbage
函数来控制垃圾回收器。
collectgarbage
函数提供了多项功能:停止垃圾回收,重启垃圾回收,强制执行一次回收循环,强制执行一步垃圾回收,获取Lua占用的内存,以及两个影响垃圾回收频率和步幅的参数。
对于批处理的Lua程序来说,停止垃圾回收collectgarbage("stop")
会提高效率,因为批处理程序在结束时,内存将全部被释放。
对于垃圾回收器的步幅来说,实际上很难一概而论。更快幅度的垃圾回收会消耗更多CPU,但会释放更多内存,从而也降低了CPU的分页时间。只有小心的试验,我们才知道哪种方式更适合。
我们应该在写代码时,按照高标准去写,尽量避免在事后进行优化。
如果真的有性能问题,我们需要用工具量化效率,找到瓶颈,然后针对其优化。当然优化过后需要再次测量,查看是否优化成功。
在优化中,我们会面临很多选择:代码可读性和运行效率,CPU换内存,内存换CPU等等。需要根据实际情况进行不断试验,来找到最终的平衡点。
最后,有两个终极武器:
第一、使用LuaJIT,LuaJIT可以使你在不修改代码的情况下获得平均约5倍的加速。查看LuaJIT在x86/x64下的性能提升比。
第二、将瓶颈部分用C/C++来写。因为Lua和C的天生近亲关系,使得Lua和C可以混合编程。但是C和Lua之间的通讯会抵消掉一部分C带来的优势。
注意:这两者并不是兼容的,你用C改写的Lua代码越多,LuaJIT所带来的优化幅度就越小。
这篇文章是基于Lua语言的创造者Roberto Ierusalimschy在Lua Programming Gems 中的Lua Performance Tips翻译改写而来。本文没有直译,做了许多删节,可以视为一份笔记。
感谢Roberto在Lua上的辛勤劳动和付出!
错误代码如下
1 2 3 4 5 |
local scrollLayer = CCScrollView:create(CCSizeMake(960,640)) local layer1 = CCLayer:create() scrollLayer:addChild(layer1) local layer2 = CCLayer:create() scrollLayer:setContainer(layer2) |
此时layer1被释放
经检查setContainer发现里面有一句this->removeAllChildrenWithCleanUp(true)
把layer1和layer2的添加顺序置换后问题消失
在实现运动中,常常需要实现一些加速度或者减速度的效果
Cocos2d-X引擎为我们提供了相应的实现接口,这样就不用再用原来的公式计算方法来实现加减速度的效果.
Ease系列的方法改变了运动的速度,但是并没有改变总体时间.如果整个动作持续5s,那么整个时间仍然会持续5s.
这些动作可以分成三类:
CCActionEase有很多子类,根据不同的缓冲公式来模拟加减速过程.缓冲动作的具体内容如下:
相关文章:http://jianyu996.blog.163.com/blog/static/1121145552012102293653906/
初学iOS开发,目前尝试用xib来构建UI
构建时有2种常用的方式:initWithNibName和loadNibNamed
我最初接触到的,是自定义一个复杂的UITableViewCell
1 2 3 |
MyCell *cell = (MyCell*)[[NSBundle mainBundle] loadNibNamed:@"MyCell" owner:self options:nil][0]; cell.data = data; |
然后在setData里做一些替换UI文字和图片的工作
于是乎,下一个ViewController我也这么做
1 2 3 |
MyViewController *viewController = (MyViewController*)[[NSBundle mainBundle] loadNibNamed:@"MyViewController" owner:self options:nil][0]; [self.navigationController pushViewController: viewController animated:YES]; |
报错 发现[[NSBundle mainBundle] loadNibNamed:@”MyViewController” owner:self options:nil][0]是一个UIView对象
没关系,UIViewController不是还提供了initWithNibName:bundle:方法么,于是乎
1 2 3 |
MyViewController *viewController = [MyViewController initWithNibName:@"MyViewController" bundle:nil];//然后在构造函数里对界面元素进行修改 [self.navigationController pushViewController: viewController animated:YES]; |
编译通过,但是奇怪的是,initWithNibName:bundle:里的自定义代码没有生效,发现xib里映射的对象全是nil
经过搜索查证,发现应该把自定义代码放在viewDidLoad里面
其实Xcode在生成文件模版的时候也提示开发者了
init的时候Custom initialization
viewDidLoad才是Do any additional setup after loading the view
转自http://wangwenhao.net/2011/10/26/the-property-in-ios-develop/
大家都知道@property和@synthesize可以自动生成某个类成员变量的存取方法,但可能对property中的一些属性不是很了解,网上的一些介绍有的不是很正确,感觉会误导新手,于是准备详细介绍一下property中的详细属性。
先介绍一下默认的情况:
readwrite:这个属性是默认的情况,会自动为你生成存取器
assign:这个属性一般用来处理基础类型,比如int、float等等,如果你声明的属性是基础类型的话,assign是默认的,你可以不加这个属性
对于assign来说,他的存取器代码是这样的:
1 2 3 4 5 6 7 8 9 10 11 |
@property (nonatomic, assign) NSString* myField - (NSString*) myField { return myField; } - (void) setMyField: (NSString*) newValue { myField = newValue; } |
natomic:默认是有该属性的,这个属性是为了保证程序在多线程情况,编译器会自动生成一些互斥加锁代码,避免该变量的读写不同步问题。
然后说一下其他的情况:
readonly:只生成getter不会有setter方法
copy:这个会自动生成你赋值对象的克隆,相当于在内存中新生成了该对象的副本,这样一来,改变赋值对象就不会改变你声明的这个成员变量了
retain:会自动retain赋值对象,具体实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
@property (nonatomic, retain) NSString* myField - (NSString*) myField { return myField; } - (void) setMyField: (NSString*) newValue { if (newValue !=myField) { [myField release]; myField = [newValue retain]; } } |
可见首先要判断一下当前myField是否就是新赋值来的对象,如果不是要将自己release掉,之后才会进行赋值及retain。
nonatomic:如果该对象无需考虑多线程的情况,请加入这个属性,这样会让编译器少生成一些互斥加锁代码,可以提高效率。
现在个人云如火如荼,而且写代码经常多线作战,为了更好的同步代码,决定把git仓库建立到个人云上去
可供选择:百度云,坚果云
1 2 3 4 5 6 7 8 9 |
//到百度云同步文件夹下 //cd ~/百度云同步盘/git_repo //建立仓库文件夹 mkdir ProjName.git cd ProjName.git //初始化仓库 git init --bare |
1 2 3 4 5 6 7 8 9 10 11 |
//到项目目录 //cd ~/Documents/ProjName //创建初始版本 git init git add. git commit -m "initial commit" //同步到百度盘的仓库里 git remote add origin localhost:/Users/yourname/百度云同步盘/git_repo/ProjName.git git push -u origin master |
等百度盘同步完成后 从别的机器上就可以直接clone下来了
适用于单人多机器的开发模式