ActionScript 3.0程式最佳化(二)
譯∕趙英傑
延續上一篇ActionScript 3.0程式最佳化(一),本文摘譯自Sean Moore撰寫的Round up of ActionScript 3.0 and Flex optimization techniques and practices,作者Sean本來列舉了51個最佳化AS3的方式,後來依據讀者的回應編輯成37個小技巧。雖然其中有些是ActionScrip程式設計師普遍知道的技巧,但整體而言,仍具有參考價值。
在翻譯過程中,我把原文底下值得參考的讀者回應加到「譯註」部分,同時也把刊載於Open Source Flash網站上的AS3 Speed tests當中的要點納入相關的「譯註」。此外,我把原文的NOT(不要)翻譯成「避免」,因為原本的程式寫法並沒有錯,只是在需要榨乾CPU運算效能的情況下,建議用另一種寫法。
1. 建立陣列(Array)時,避免使用 new 運算子:
var a = [];
避免寫成:
var a = new Array();
2. 建立陣列會耗費比較多的時間,請謹慎使用:
var vanityCollection01 : Array = new Array(); var vanityCollection02 : Array = new Array(); var vanityCollection03 : Array = new Array(); var vanityCollection04 : Array = new Array();
3. 最快速的陣列複製方式(使用concat()方法):
var copy : Array = sourceArray.concat();
4. 設定單一陣列元素值的執行速度比較慢:
employees.push( employee ); employees[2] = employee;
5. 讀取陣列元素值的速度是設定其值的兩倍:
var employee : Employee = employees[2];
譯註:Vector 結構類型(另一種形式的「陣列」),比 Array 效率高。請參閱ActionScript 3.0程式最佳化(一)裡的「Vector資料類型簡介」一節說明。
6. 替無須建立物件實體的類別,宣告 static(靜態)成員:
StringUtils.trim( 'text with space at end ' ); 類別定義: package { public final class StringUtils { public static function trim( s : String ) : String { var trimmed : String; // 方法的程式碼 return trimmed; } } }
譯註:執行 static 方法比一般物件的方法更耗費時間。
7. 在應用程式的執行週期中不會改變的內容,應使用 const 將它宣告為常數:
public const APPLICATION_PUBLISHER : String = 'Company, Inc.';
8. 不會被繼承的類別(亦即,其下沒有子類別),應該用 final 關鍵字宣告:
public final class StringUtils
9. 方法與變數的名稱長度,不影響ActionScript 3.0的效能(其他程式語言也一樣):
someCrazyLongMethodNameDoesntReallyImpactPerformanceTooMuch();
譯註:有人持相反的看法,認為過長的變數名稱會影響處理效率。
10. 把多個指派設定敘述寫成一行,不會增加效能(其他程式語言也一樣):
var i=0; j=10; k=200;
11. if 條件式和 switch 條件敘述的記憶體用量相同:
if ( 條件式 ) { // 處理條件 }
記憶體用量等同於:
switch ( 條件式 ) { case 'A': // 處理條件 A 的邏輯 break; case 'B': // 處理條件 B 的邏輯 break; }
譯註:if 條件式的執行效率優於 switch。
12. 把 if 條件判斷式中,依照最有可能成立(true)的判斷結果依序安排:
if ( 經常發生的情況 ) { // 處理經常發生的程式邏輯 } else if ( 偶爾發生的情況 ) { // 處理偶爾發生的程式邏輯 } else { // 處理較不常發生的程式邏輯 }
13. ActionScript虛擬機器(簡稱AVM)會在迴圈中,把 int(整數)類型轉型成 Number(數字)類型(Flash Player裡的AVM會隨著版本更新而改進,在第10版中,執行 int, uint 和 Number 轉換時,不會像以前那樣慢)。
14. 解決物件類型變換(promotion)、未知類型以及不正確類型的問題。
15. 審慎使用 uint 類型,它的效能也許不佳(Flash Player裡的AVM會隨著版本更新而改進,在第10版中,執行 int, uint 和 Number 轉換時,不會像以前那樣慢)。
var footerHex : uint = 0x00ccff;
16. 迴圈中的迭代子(iterator,亦即底下程式片段裡的變數 i),請宣告成 int 類型:
for (var i: int = 0; i < n; i++)
避免寫成:
for (var i: Number = 0; i < n; i++)
譯註1:while 迴圈的執行效能高於 for 迴圈。
譯註2:i++ 執行效率遠高於 i = i + 1。實際上,連續執行四次 i++,還比執行一次 i = i + 4 快了 50%!此外,a += b 的效率也高於 a = a + b。然而,減號(-)運算子的效率就沒有明顯的差異,也就是說,i-- 並不會比 i = i - 1還快。
17. 不要在 int 類型資料中使用小數:
var decimal : Number = 14.654;
而非:
var decimal : int = 14.654;
譯註1:實際上,int 類型只會保存整數。
譯註2:取得最小整數、最大整數和四捨五入的 Math.floor() 以及 Math.round() 靜態方法的執行效率不佳,建議用底下的敘述取代它們:
int(n); // 相當於 Math.floor(n); int(n) + 1; // 相當於 Math.ceil(n); int(n + 0.5); // 相當於 Math.round(n);
請注意!上面的 Math.floor() 與 Math.ceil() 的替代語法僅適用於正數,底下是負數時的運算情況:
var test1:int = int(-1.5); //test1 的值為 -1 var test2:int = Math.floor(-1.5); //test2 的值為 -2
取得負值的最小整數和最大整數的解決方案如下(還是比 Math 物件的靜態方法快!):
var n:Number = -1.5; var test1:int = int(n) + (n >> 0); // 相當於 Math.floor(n); //test1 的值為 -2 var test2:int = int(n+1) + (n >> 0); // 相當於 Math.ceil(n); //test2 的值為 -1
譯註3:底下的取絕對值敘述替代方案,會比 Math.abs() 快很多:
// 使用 Math.abs() 取絕對值 var n:Number = -1.5 var test:Number = Math.abs(n); // 底下的方案比較快! var n:Number = -1.5; if (n < 0) n = -n;
18. 用乘法代替除法,這樣寫不好:5000/1000,乘上小數點比較快:5000*0.001
譯註:使用位元移位( Bitwise shift)運算子的運算效率更高:
// 使用右移運算子來除以2: trace(10 >> 1); // 輸出:5 // 使用左移運算子來乘以2: trace(10 << 1); // 輸出:20
19. 將運算值儲存在變數中,供 for 和 while 迴圈敘述使用,不要在迴圈中重複計算:
for (..){ a * 180 / Math.PI; } 應該在迴圈外部定義:toRadians = a*180/Math.PI;
20. 避免在迴圈中進行運算或呼叫方法:
var len : int = myArray.lengh; for (var i=0;i<len;i++){}
避免寫成:
for (var i=0;i< myArray.lengh;i++){ }
21. 用 RegEx(正規表達式)來驗證字串;用字串物件的方法來搜尋文字:
// 使用正規表達式來驗證美國郵遞區號的例子 private var regEx:RegExp = /^[A-Z][0-9][A-Z] [0-9][A-Z][0-9]$/i; private function validatePostal( event : Event ) : void { if( regEx.test( zipTextInput.text ) ) { // 處理格式不符的程式碼 } } // 使用字串的方法來搜尋文字 var string : String = 'Search me'; var searchIndex : int = string.indexOf( 'me' ); var search : String = string.substring( searchIndex, searchIndex + 2 );
22. 重複使用物件(如:DisplayObject, URLLoader, …),以便維持記憶體的使用效率。
23. 遵循 Flex 的組件模式:
createChildren(); // 建立子物件 commitProperties(); // 設定屬性 updateDisplayList(); // 更新顯示物件清單
24. 不得已時才使用 Datagrids 組件(先確認你無法用一般的 List 組件來完成)。
25. 避免在可捲動的資料區域使用 Repeater 組件。
26. 避免使用 setStyle()(Flex框架中,最消耗效能的呼叫之一)。
27. 使用太多容器(container),將大幅影響應用程式的效能:
<mx:Panel> <mx:VBox> <mx:HBox> <mx:Label text="Label 1" /> <mx:VBox> <mx:Label text="Label 2" /> </mx:VBox> <mx:HBox> <mx:Label text="Label 3" /> <mx:VBox> <mx:Label text="Label 4" /> </mx:VBox> </mx:HBox> </mx:HBox> </mx:VBox> </mx:Panel>
28. 你並不總是需要用容器當作組件的最上層(top-level)標籤,完全合法的組件並不需要最上層容器。
<mx:Image xmlns:mx="http://www.adobe.com/2006/mxml" source="avatar.jpg" width="200" height="200" />
29. 移除不必要的容器組合,以便減少嵌套的容器。
30. 避免在標籤元素裡面使用 VBox。(減少贅餘程式)
<mx:Panel> <mx:Label text="Label 1" /> <mx:Label text="Label 2" /> </mx:Panel> <mx:Panel> <mx:VBox> <mx:Label text="Label 1" /> <mx:Label text="Label 2" /> </mx:VBox> </mx:Panel>
31. 避免在 mx:Application 標籤元素中使用 VBox。(減少贅餘程式)
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx=http://www.adobe.com/2006/mxml> <mx:Label text="Label 1" /> <mx:Label text="Label 2" /> </mx:Application>
避免寫成:
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx=http://www.adobe.com/2006/mxml> <mx:VBox> <mx:Label text="Label 1" /> <mx:Label text="Label 2" /> </mx:VBox> </mx:Application>
32. 將 recycleChildren 屬性設定成 true 來改善 Repeater 物件的效能(重用之前建立的物件而非重新建立新的)。
<mx:Script> <![CDATA[ [Bindable] public var repeaterData : Array = ["data 1", "data 2"]; ]]> </mx:Script> <mx:Repeater id="repeater" dataProvider="{repeaterData}"> <mx:Label text="data item: {repeater.currentItem}"/> </mx:Repeater>
33. 影格速率(frameRate)設定在 60fps 或更低的數值。
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx=http://www.adobe.com/2006/mxml frameRate="45"> </mx:Application>
34.避免逐格操控多個顯示物件。
35. ENTER_FRAME 事件的效率比 Timer 事件高。
public function onEnterFrame( event : Event ) : void { } private function init() : void { addEventListener( Event.ENTER_FRAME, onEnterFrame ); }
避免這樣寫:
public function onTimerTick( event : Event ) : void { } private function init() : void { var timer : Timer = new Timer(); timer.start(); timer.addEventListener( TimerEvent.TIMER, onTimerTick ); }
36. 要延緩到多個影格之後才建立物件,請使用:
<mx:Container creationPolicy="queued"/>
37. 若要隱藏物件,請使用 visible 屬性:
loginButton.visible = false;
而非:
loginButton.alpha = 0;
延伸閱讀
- ActionScript 3.0程式最佳化(一)
- AS3 Speed tests (osflash.org):有完整的測試程式片段提供參考
- AS3 Flash Efficient Code Techniques, Vectors in Flash 10, Faster JPEG Encoding, Other Optimization Notes
- 更多AS3最佳化的相關文件,請參閱原文底下的參考文件(References)清單。
if 條件式的執行效率優於 switch
你有没测试过的,我测试后是switch优于if,别教坏人了
谢谢你的回应,也许你可以跟原作者讨论一下,看看到底谁错了
也请你提供测试代码让大家观摩学习,谢谢!
😎
大大 你在timer的部份寫說ENTER_FRAME 的效率高
但是我現在遇到的一個問題是
人物在控制方面的延遲,因為當中有加上timer的控制
我應該如何去用呢? 因為我會用timer的原因是 人物的動畫都沒播完就是沒看到他動的感覺,這問題該如何解決好
hi,
造成程式出狀況的因素很多,我不知道如何協助你,拍謝。
jeffrey