ActionScript 3.0程式最佳化(二)

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; 

延伸閱讀

Posts created 483

4 thoughts on “ActionScript 3.0程式最佳化(二)

  1. if 條件式的執行效率優於 switch
    你有没测试过的,我测试后是switch优于if,别教坏人了

  2. 谢谢你的回应,也许你可以跟原作者讨论一下,看看到底谁错了 :mrgreen:

    也请你提供测试代码让大家观摩学习,谢谢!

  3. 😎

    大大 你在timer的部份寫說ENTER_FRAME 的效率高
    但是我現在遇到的一個問題是
    人物在控制方面的延遲,因為當中有加上timer的控制
    我應該如何去用呢? 因為我會用timer的原因是 人物的動畫都沒播完就是沒看到他動的感覺,這問題該如何解決好

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

Related Posts

Begin typing your search term above and press enter to search. Press ESC to cancel.

Back To Top