简单的for循环

遍历数组

while循环遍历

遍历字典

 

 

字典就相当于OC里的NSDictionary,根据视频里的例子

好吧,这里说一下我遇到的一个问题

我觉得我既然开始时候没指定dict的value类型,就应该让我随便添加,我认为这应该算是个bug吧,不知道苹果是怎么考量的,为什么混合的value类型就不允许添加

Swift的数组用[]可以直接声明,里面的元素可以混着写

声明一个空数组

 

一般的字符串之间连接可以直接用加号(+)连接

swift也提供了一种类似于占位符的连接方式,可以连接字符串和其他类型

 

swift支持类型推导,一般用var不需要指定类型

需要指定类型时,可以这样

 

Swift里变量用var,常量用let来定义

变量声明后可以随时改变值,而常量只能赋值一次,再赋值则会直接报错

打开Xcode6,new Project的时候Language选择Swift

就可以看到很简单的一段

就这么简单

我是跟着极客学院的Swift教程学的

顺边说一下,他们的视频还是不错的,没有太多废话

  1. Hello World
  2. 变量与常量
  3. 类型
  4. 字符串连接
  5. 数组
  6. 字典
  7. 循环
  8. 流程控制
  9. 函数
  10. 面向对象
  11. 使用Swift开发iOS项目

教程看完了,总体来说Swift还是非常方便的,有编写脚本语言的爽快感,但目前xcode6beta版的代码提示功能还非常弱,期待正式版

教程里什么时候加标签什么时候不加并没有说清楚

总结了一下,类方法不需要加,构造方法需要加

转自http://www.swfdiy.com/?p=1663

在quick-cocos2dx里, 通过class() 函数进行类的定义

下面贴出quick-cocos2d-x-2.2.1-rc 里 class()的源码

我们先考虑最简单的情况, 在没有继承的情况下定义一个类ClassA

这种情况下, ClassA 跟InstanceA的关系如下图

InstanceA的metatable为ClassA, 而且ClassA.__index = ClassA, 因此,对于InstanceA找不到的属性, 将在ClassA里进行查找。 需要注意的是,ClassA里的属性比如field1, 是相当于类变量的概念,所有实例都公用该属性,并非各自维护该字段的拷贝。

下面如果要再定义ClassB, 从ClassA 进行继承

这种情况下, ClassB 跟InstanceB, ClassA的关系如下图

 

同理,InstanceB里没有定义的属性, 将首先在ClassB里进行搜索, 如果ClassB里搜不到, 将向上追寻到ClassA里。

上面考虑的都只是针对纯Lua类, 在实际情况中, 我们需要对C++里生成的对象(userdata)进行扩展, 可以如下定义

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继承的情况

关系图:

===================

最先是看到下面这篇文章的,也写的很棒

相关文章:http://childhood.logdown.com/posts/169509/detailed-quickcocos2dx-inheritance-mechanism

转自:http://wuzhiwei.net/lua_performance/

前言

Lua是一门以其性能著称的脚本语言,被广泛应用在很多方面,尤其是游戏。像《魔兽世界》的插件,手机游戏《大掌门》《神曲》《迷失之地》等都是用Lua来写的逻辑。

所以大部分时候我们不需要去考虑性能问题。Knuth有句名言:“过早优化是万恶之源”。其意思就是过早优化是不必要的,会浪费大量时间,而且容易导致代码混乱。

所以一个好的程序员在考虑优化性能前必须问自己两个问题:“我的程序真的需要优化吗?”。如果答案为是,那么再问自己:“优化哪个部分?”。

我们不能靠臆想和凭空猜测来决定优化哪个部分,代码的运行效率必须是可测量的。我们需要借助于分析器来测定性能的瓶颈,然后着手优化。优化后,我们仍然要借助于分析器来测量所做的优化是否真的有效。

我认为最好的方式是在首次编写的时候按照最佳实践去写出高性能的代码,而不是编写了一堆垃圾代码后,再考虑优化。相信工作后大家都会对事后的优化的繁琐都深有体会。

一旦你决定编写高性能的Lua代码,下文将会指出在Lua中哪些代码是可以优化的,哪些代码会是运行缓慢的,然后怎么去优化它们。

使用local

在代码运行前,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的预编译会产生一条指令:

但是若a和b都没有声明为local变量,则预编译会产生如下指令:

所以你懂的:在写Lua代码时,你应该尽量使用local变量

以下是几个对比测试,你可以复制代码到你的编辑器中,进行测试。

math.sin赋给local变量sin

直接使用math.sin,耗时1.11秒;使用local变量sin来保存math.sin,耗时0.76秒。可以获得30%的效率提升!

关于表(table)

表在Lua中使用十分频繁,因为表几乎代替了Lua的所有容器。所以快速了解一下Lua底层是如何实现表,对我们编写Lua代码是有好处的。

Lua的表分为两个部分:数组(array)部分和哈希(hash)部分。数组部分包含所有从1到n的整数键,其他的所有键都储存在哈希部分中。

哈希部分其实就是一个哈希表,哈希表本质是一个数组,它利用哈希算法将键转化为数组下标,若下标有冲突(即同一个下标对应了两个不同的键),则它会将冲突的下标上创建一个链表,将不同的键串在这个链表上,这种解决冲突的方法叫做:链地址法。

当我们把一个新键值赋给表时,若数组和哈希表已经满了,则会触发一个再哈希(rehash)。再哈希的代价是高昂的。首先会在内存中分配一个新的长度的数组,然后将所有记录再全部哈希一遍,将原来的记录转移到新数组中。新哈希表的长度是最接近于所有元素数目的2的乘方。

当创建一个空表时,数组和哈希部分的长度都将初始化为0,即不会为它们初始化任何数组。让我们来看下执行下面这段代码时在Lua中发生了什么:

最开始,Lua创建了一个空表a,在第一次迭代中,a[1] = true触发了一次rehash,Lua将数组部分的长度设置为2^0,即1,哈希部分仍为空。在第二次迭代中,a[2] = true再次触发了rehash,将数组部分长度设为2^1,即2。最后一次迭代,又触发了一次rehash,将数组部分长度设为2^2,即4。

下面这段代码:

与上一段代码类似,只是其触发了三次表中哈希部分的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秒:

如果我们在创建表的时候就填充好它的大小,则只需要0.75秒,一倍的效率提升!

所以,当需要创建非常多的小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秒:

我们可以用table来模拟buffer,下面的代码只需花费0.72秒,9倍多的效率提升:

3R原则

3R原则(the rules of 3R)是:减量化(reducing),再利用(reusing)和再循环(recycling)三种原则的简称。

3R原则本是循环经济和环保的原则,但是其同样适用于Lua。

Reducing

有许多办法能够避免创建新对象和节约内存。例如:如果你的程序中使用了太多的表,你可以考虑换一种数据结构来表示。

举个栗子。 假设你的程序中有多边形这个类型,你用一个表来储存多边形的顶点:

一百万个顶点时,内存将会由153.3MB减少到107.6MB,但是代价是代码的可读性降低了。

最变态的方法是:

一百万个顶点,内存将只占用32MB,相当于原来的1/5。你需要在性能和代码可读性之间做出取舍。

在循环中,我们更需要注意实例的创建。

Reusing

如果无法避免创建新对象,我们需要考虑重用旧对象。

考虑下面这段代码:

下面这段代码重用了表:

另一种方式的重用,则是在于缓存之前计算的内容,以避免后续的重复计算。后续遇到相同的情况时,则可以直接查表取出。这种方式实际就是动态规划效率高的原因所在,其本质是用空间换时间。

Recycling

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上的辛勤劳动和付出!