2012-10-13 56 views
2

我正處於發佈我的第一個遊戲的最後階段,並且在運行Instruments:Leaks & Allocations後,我發現由於保留週期而導致代碼泄漏。我正在使用Cocos2d 2.0,並使用ARC編譯我的應用程序,並且應該提及我在ARC之前啓動了該項目,並使用Xcode重構工具將其轉換。我的遊戲在每個屏幕上有幾個動畫對象,每個對象都有一個(1-7)動畫「變體」的對象(即,穀倉打開以顯示一次馬,而斑馬另一次)。我有一個代表每個動畫的類,以及每個變體的另一個類。該變體從一系列幀創建一個CCAnimation,然後創建一個動作,只要在正確的區域接收到觸摸事件,就會運行該動作。這個行動是什麼導致我的保留週期。我對動作伊娃聲明如下所示:保留週期CCAction和CCCallFunc/CCCallBlock

@interface AnimationVariant : NSObject 
{ 
@private 
    CCAction* _action; 
... 
} 
@property (readonly, nonatomic) CCAction* action; 
... 

-(void) setupActionWithMask:(int)mask 
        cycles:(int)cycles 
        hasScale:(bool)hasScale 
         scale:(float)scale 
       masterScale:(float)master_scale 
        animationFrames:(NSArray*) frames 
        duration:(float)duration 
        andBlock:(VoidBlock)block; 

@end 

在setupActionWithMask方法的實現,我建立CCActions的一個NSMutableArray,的ActionList。 CCActions的順序取決於ARGS而異,但通常它看起來是這樣的:

[actionList addObject:[CCScaleTo actionWithDuration:0.0f scale:scale]]; 
[actionList addObject: [CCAnimate actionWithAnimation:animation] ]; 
[actionList addObject:[CCScaleTo actionWithDuration:0.0f scale:master_scale]]; 
[actionList addObject: [CCCallBlock actionWithBlock:block]]; 

我創建這樣的動作:

_action = [CCSequence actionMutableArray:actionList]; 

消費類創建一個AnimationVariant實例,設置其屬性,調用setupActionWithMask,並在動作完成時傳入它想要執行的塊。當消費類要播放動畫變種,它這樣做是這樣的:

[self runAction: variant.action]; 

我想聲明_action爲:

CCAction* __unsafe_unretained _action; 

當然,這打破了保留週期,但動作被銷燬,並且不再需要時(這是您所期望的,因爲__unsafe_unretained不會保留)。我知道__weak是推薦的解決方案,但是因爲我針對的是iOS 4或更高版本,所以我認爲它不適用於我。

我的代碼中有另一個保留週期,就像這個一樣,也是由於保留了(自動用ARC當然)包含CCCallFunc/CCCallBlock的CCSequence而引起的。我通過在需要的時候重新創建它來解決這個問題,在這種情況下我也可以這樣做,但這些動畫在整個遊戲中可能會觸發幾百次,所以我希望按照推薦的Cocos2d最佳實踐並保留這些行爲。

謝謝!

回答

2

保留行爲不是最佳實踐。這甚至不是好的做法。儘管很多人都非常推薦它,但很不幸。

保留動作在很多情況下都有效,但在其他情況下會導致物體泄漏。我猜你的情況可能就是其中之一。

由於您的目標是iOS 4,因此您不能使用弱引用。但是,除非必須針對剩餘的少數第一代和第二代設備,否則您應該重新考慮。否則,谷歌爲iOS 5採用率。少數尚未更新的設備遠低於合理的門檻,特別是如果您認爲那些用戶可能不會購買(許多)應用程序(不再)。

既然您的意思是CCCallFunc,請確保您不使用它們並用CCCallBlock代替。CCCallFunc與ARC一起使用並不安全,特別是當您必須__bridge_transfer將數據對象轉換爲void *時(也是不好的做法)。

始終有必要的橋回到原始對象永遠不會發生,然後ARC沒有機會清理該對象。使用CCCallFunc時,可能會發生這種情況,當您運行調用func操作但在調用回調選擇器之前停止操作時,例如通過更改場景或停止操作/序列。

的Cocos2D也容易保留週期,如果你不遵循這個規則:

  • 任何節點應該只保留另一個節點是其子女或孫子女的一個

在所有其他case(即節點保留(grand)父節點或兄弟節點),你必須確保在 - (void)清除方法中沒有這些引用。在 - (void)dealloc中這麼做太遲了,因爲當存在一個保留週期時,對象將永遠不會釋放。

+0

非常感謝。我已經得出了相同的結論,並重寫了代碼,不保留所有的操作,也沒有看到性能下降(並非我真正期望的)。我不知道 - (void)清理方法 - 不知道我是如何錯過的。但是我確實試圖在dealloc中淘汰伊娃,正如你所說 - 那時已經太晚了。我已經切換到塊 - 但我也沒有意識到CCCallFunc與ARC不安全 - 我只是認爲塊更清潔。 –

+1

實際上,您根本無法在Xcode 4.5上開發第一代和第二代設備(armv6設備)。最低目標版本是4.3。 – newacct

+0

@newacct - 是的,幾天後我去購買二手Gen Touch進行測試時發現了這個問題。但它確實顯示你可以瞄準4.3,但仍然不支持弱引用 - 除非我錯了。 –