mm24
mm24

Reputation: 9586

Properties and ARC

I am working with ARC and Cocos2d 2.0 as static library (which does not use ARC and is compiled as a separate target). I translated an old project (made without ARC) and I am wondering whether declaring properties in this way has some potential retain cycle issue:

@interface PlayerData : NSObject <NSCoding> {
}


//Is ok to save this as if the game gets paused you may want to save this.
@property (readwrite, nonatomic) int numberOfHits;
@property (readwrite, nonatomic) bool everBeenHitInCurrentLevel;
@property (readwrite, nonatomic) int hitsForOneHearth;

I noticed that my various scenes build up memory over time. Also I added a CCLOG call in the release method of the CCLayer method (MyScene : CCLayer) and it never gets called. That's how I create the scene (which I replace using the "[CCDirector sharedDirector] replaceScene" method

+ (id) sceneWithLevelName:(LevelName)name
{
    CCScene *scene = [CCScene node];        
    ShooterScene * shooterLayer = [[self alloc] initWithId:name];
    [scene addChild:shooterLayer];


    return scene;    
}

EDIT: As I realized that I stupidly did NOT include an example with Objects and used only primitive data type I will here paste some snippets of elements in my scenes: CharacterSelection, ShooterScene and PlanetSelectionMenu:

//ShooterScene
@interface ShooterScene : CCLayer {
    HudLayer * hudLayer;
    ....
}

@property(readwrite, nonatomic) CCSpriteBatchNode* backgroundAndEnemiesBatchNode;
@property(readwrite, nonatomic) ShipEntity* playerShip;
... etc..


Please note that I do not declare member variables for properties like playerShip and backgroundAndEnemiesBatchNode beause, as far as I can understand, should suffice the property declaration (but please correct me if I am wrong or if the approach may cause issues). 

//CharacterSelectionScene
@interface CharacterSelectionScene : CCLayer {
    int currentlySelectedCharacterSpriteTag;
    CCSprite * lights;
    CCLayer * spritesLayer;    
    ...
}

//PlanetSelectionMenu
#import <Foundation/Foundation.h>
#import "cocos2d.h"

@interface PlanetSelectionMenu : CCLayer {
    CCLayer * backgroundLayer; // Added background images here
    CCLayer * alwaysPresentItems;
}

+ (id) scene;

@end

Please note that each time I go from PlanetSelectionMenu to CharacterSelectionScene -and vice versa- memories increases. However in this case I have not used any properties but "just" added objects (CCSprites) to layers and batchnodes.

EDIT 2: Here is what I see in Allocation when running through Menu->CharacterSelection->PlanetSelectionScene etc.. it seem that on avarage the LiveBytes are 4MB and, as I see only one scene at the time then I assume that there are no retain cycles. Then why do I get those nasty LOW memory messages?

enter image description here

Upvotes: 2

Views: 188

Answers (1)

James Webster
James Webster

Reputation: 32066

Although the default property attribute is now strong, it can't be those properties that are causing a retain cycle as those are primitive types and would default to assign

Three other common methods of introducing retain cycles jump to mind:

  • Are you implementing the delegate pattern anywhere (@protocols). Are your delegate's always weak references where necessary?
    -(id) initWithDelegate:(id) target
    {
        ...
        _target = target;   //_target should be a weak reference
        ...
    }
  • Do any of your children nodes reference their parents?
    -(id) initWithParent:(CCNode*) parentNode
    {
        ...
        _parent = parentNode; //_parent should be a weak reference.
        ...
    }
  • Do any blocks reference self
   ^{[self callSomeMethod];}

should use a weak reference to self:

   __weak typeof(self) selfReference = self;
   ^{[selfReference callSomeMethod];}

I usually find that the best way to find leaks with ARC is NOT to use the Leaks tool, but the Allocations tool. Since all of my scenes tend to have the word "scene" in their symbol, I filter by the word scene.

Since you are using replaceScene you should only have one scene alive at a time (excepting during a transition), so you should only see one object in the object summary.

enter image description here

If you do have a scene that's hanging around, I usually find it's best to look at that objects retain history. From here, I pair up each retain with a corresponding release until I find the culprit(s) that are retaining and not releasing my scene. More often that not it's an obvious retain cycle with a block, or a property declared as strong instead of weak.

enter image description here

Upvotes: 6

Related Questions