Corona中文站

强大、易学的跨平台(iOS/Android)开发框架。QQ群1:74390406(满) 群2:221929599

导航

五分钟学会Corona(二十七) - 性能和优化
当你开发你的应用程序时,你总是会考虑你的设计选择,将会怎样影响到你程序的性能。尽管近来计算能力有所改善,但移动设备仍然要面对一些根本性制约:处理能力、内存使用以及电池寿命等。所以,在性能和优化方面最好的考虑,也不仅仅是加快响应速度,也包括最小化内存使用,和最大化电池寿命。


高效使用内存

在移动设备上,内存是一个关键资源。有些设备会当你吃掉太多内存的时候,强行关闭你的应用程序。


• 消除内存泄露. 你的应用程序不应该造成内存泄露。允许内存泄露意味着你的应用程序可能在之后它需要内存的时候无法得到内存。尽管Lua使用自动内存管理,但在你的代码里还是有可能发生内存泄露。 (see Memory Allocation).例如全局变量永远不会被回收,它需要你告诉lua,以 (globalVar = nil)的方式来回收这些变量。如果一个全局变量是 table,那么这个table中的每一个项目都只有在你使用nil out时,才会被释放。(globalVar.item = nil)

• 让资源文件尽量小. 你的应用程序使用的文件通常驻留在磁盘上。它们必须在使用前被加载到内存中。图片也应该尽可能小。例如,为了创建一个连环画式的全屏动画。有人尝试加载许多和屏幕大小一致的图片,尽管变化的只是前景中的一些元素而已。那么更好的做法,是把前景从背景中分隔出来,做成尽量小的图片,而背景只用单独一张屏幕大小的图片。

• 延迟加载资源. 直到实际需要一个资源的时候才去加载它。 初看上去,预先读取资源似乎更加节省时间;然而这样做却事与愿违,因为这样会让设备出现低内存状况。另外,你的应用程序也许从来都没有使用过这个资源,预取到内存中不但浪费时间,也浪费内存。

• 从显示层次中删除对象. 当一个display object被创建,它会被隐式加入显示层次中。当你不再需要一个display object时,你应该从显示层次中将其删除,尤其是这个对象包含图片时。这使得display object有机会被垃圾回收。然而,不能肯定这个对象就应该立刻被回收,因为其他变量(如果没释放)可能正引用着这个对象。这种情况下,对象不渲染在屏幕上但是它的内存仍然没有被释放。


例子

下面有一个关于内存泄露是如何发生的例子。左边的代码在你点击它时从显示层次中删除一个矩形对象,但是矩形对象的内存发生了泄露,因为变量rect仍然引用着这个对象。因为rect是一个全局变量,它引用的display object将不被释放,尽管这个矩形不再在屏幕上渲染。


解决这个问题的一个方法是,修改removeOnTap函数,添加一行来nil-out这个引用(rect=nil)。一个更好的解决方案是,把全局变量变成局部变量,因此你就不用在显式的nil-out这个引用。把rect变成局部变量,就是右边代码的解决办法。





降低能耗

因为尺寸很小,所以移动设备的电池的续航时间是有限的。你可以通过尽量少使用下面的特性,以提高电池的续航时间。

• 网络流量 (Wi-Fi和电话基带)

• GPS

• 加速计

• 磁盘访问 (读/写文件)

你将不可避免使用这些特性来创建良好的用户体验。然而,当你设计你的应用程序时,合理的使用这些特性,提高用户设备的电池续航。



网络

在所有的活动中,网络访问对电量的消耗最大。遵循下面的原则,你可以最大限度减少网络流量的影响:

• 不要轮询. 只在需要的时候接连网络服务

• 最小化数据尺寸. 优化你的要传输的数据,以使他尽可能的小

• 突发传输. 传输数据的时间越久,能源消耗越大。 所以应该一次性传输所有数据,而不要在一段时间内把数据包分散传输。

• 如果你访问位置信息,一旦你得到你需要的数据,就立刻停止搜集位置信息。位置数据来自使用GPS,电话和wifi网络,所以为了节省电力最好的办法只有在你需要的时候再搜集位置信息。.


CPU

最小化电力消耗的另一个途径,是优化你应用程序的运行时间。一些经验法则,包括延时执行、在必须执行的时候在执行,好过做太多暂时不会用的工作。


图像

组对象


如果你打算 设置对象的一个分支属性(例如alpha)设置一个相同的值,最好是把对象添加到组上,然后修改组的属性。这样更容易编码,并且能优化你的动画。


把对象组织到组当中的另一个好处,是可以把你的内容组织到屏幕上。(参看 Managing Screens )


关闭不可见对象的动画


听上去这是显然的,但是还是很容易忽略这个事实,就是你运行一些不可见或者屏幕外的动画。


例如,你应该有一个组,为主屏幕存储所有的对象。在这个主屏幕组中,有一些你通过为“enterFrame”事件注册listener实现自定义动画的子对象(也许一个弹球,也许一个旋转齿轮)产生的动画。当用户转去其他屏幕时,你实际上已经设置这个组为不可见的了(isVisible被设为 false)。不幸的时,这个listener还将继续计算,却产生不了任何可见的效果。参看 Pausing and Restarting Animations.


解决之道,是当你转而切换到新的屏幕时,应该删除这个事件的listener,然后当你回到这个主屏幕的时候注册这些listener。


优化图片大小


小心使用大图片,尤其是全屏图片。它们在两个地方影响性能:1,它们耗费更多的事件来加载图片,所以影响到程序的响应速度。2,它们耗费过多的内存;如果内存消耗过大,一些设备甚至会强行关闭你的程序。所以,当你不再需要它们的时候,应该从它们的父组中删除它们:





启动时最小化初始工作


当你的程序启动时,通常你的main.lua文件包含很多加载代码,来添加图片到屏幕上、为响应用户事件或帧事件安装listener等等。如果你的加载代码太大,你的用户将会看不到任何屏幕的更新,因为直到一个代码快执行完毕之前,屏幕是不会刷新的。




Lua: 最佳实践


简单修改你的lua代码,可能会产生巨大的效益。在下面一些例子中,可以看到通过非常简单的修改,让你的lua代码挤出额外的性能:


使用local (如避免全局变量)

避免使用全局变量。lua中,如果你使用全局变量,你将会牺牲性能。当你疑惑的时候,尽量把你的变量声明成local的。


这一点甚至只用于函数。在lua中,函数也是一种变量。在很长的循环中,把函数赋给一个本地变量是很不错的。在下面的例子中,左边的代码要比右边的慢30%!





在某些情况下,不能把全局变量缓存进纯局部变量。例如,在一个函数内,你也许不能把某个函数作为局部变量存入另一个函数变量。这时候,你可以用使用函数作用域之外的局部变量。“external locals”(非局部)虽然没有纯局部变量那么快,但是也比全局变量快。


在下面的例子中,我们可以通过在foo函数外声明一次sin来优化左边的代码:





一样,全局变量的版本要慢30%。


数学:快vs慢

乘法 x*0.5 比 除法x/2更快


x*x 比 x^2更快


插入对象到数组中

简短的行内表达式速度比函数调用快。例如,当添加一个元素到数组中的时候,t[#t+1] = item 比table.insert(t,item)要快的多。


常量累加

常量累加是在编译期简单常量表达式的处理。例如这个语句i=111+111,和i=222其实一样快,因为编译器会预先计算好这个表达式的结果。


要想充分利用这个优势,你需要清楚的直到编译器何时会做这样的计算,以及何时不会。第一,编译器没有足够聪明到可以知道变量的值。第二基于这个原则,常量计算的顺序很重要,在lua中a+b+c 等价于 (a+b)+c。所以表达式 1+2 + x,速度和 (1+2)+x是一样的,因为都被优化成3+x。但是x+1+2,则相当于(x+1)+2,则没有充分利用优化特性。


在局部变量中缓存属性


如果你经常去访问一个table的属性,却不改变他的值,那你就应该缓存这个值。因为在table中查找属性,会有轻微的性能损失。对于被Corona SDK的API创建的对象--如用display.newImage()返回的display object--性能损失会更大。



<< 五分钟学会Corona(二十六) - 本地 UI五分钟学会Corona(二十八) - 模块和包 >>

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

最近发表

Powered By Z-Blog 1.8 Walle Build 100427 Copyright 2011-2015 BuildApp.Net. All Rights Reserved.