Conditional alloc/dealloc? [Objective-c and Cocos2D] - objective-c

What if I want to alloc a class inside another and I want to reference it easily, but sometimes this class would not need to be alloc'd, therefore not dealloc'd. How is this done? Can I put a conditional inside dealloc so it doesn't have to be released?
In more detail, I'm using Cocos2D. I have player ability classes that may or may not need to be allocated. In my init:
// Abilities
if(abilityRushH == 0){
objects = [theMap objectGroupNamed:#"oj"];
startPoint = [objects objectNamed:#"item_ability_rushH"];
x = [[startPoint valueForKey:#"x"] intValue];
y = [[startPoint valueForKey:#"y"] intValue];
rushH = [[RushHorizontal alloc] init];
[self addChild:rushH.rushHSpriteSheet];
rushH.rushHSprite.position = ccp(x,y);
}
if(abilityRushV == 0){
objects = [theMap objectGroupNamed:#"oj"];
startPoint = [objects objectNamed:#"item_ability_rushV"];
x = [[startPoint valueForKey:#"x"] intValue];
y = [[startPoint valueForKey:#"y"] intValue];
rushV = [[RushVertical alloc] init];
[self addChild:rushV.rushVSpriteSheet];
rushV.rushVSprite.position = ccp(x,y);
}
Cocos2D needs to keep the reference so it can scroll with the map. But if I'm not alloc'ing it, how do I NOT dealloc?

Since you are talking of releasing it in dealloc, there will be an instance variable for this. Now when any instance of an Objective-C class is allocated, all its objects are nil and c types are set to 0 (or equivalent values). So you don't need to put any extra effort if the object of your class isn't instantiated as the instance variable will be nil at dealloc and so release message sent to it will have no effect.

Make sure the optional variable is nil when its not needed, and do a nil check before dealloc'ing.

Related

Why the local variables are always strong?

According to the notes in class, it is said that the local variables are always strong. I really wonder the reason for it. Why the local variables are always strong.
BTW, I am learning blocks. As far as I know, the local variables is supposed to be used and declared inside the method or blocks. However, the local variables I see when using blocks are declared outside the method, but used inside the block. Therefore, I am really curious about it.
The code where the local variable is outside the method looks like this:
_block BOOL stoppedEarly = NO; // this is the local variable
double stoppedValue = 52;
[aDictionary enumerateKeysAndObjectsUsingBlock: ^(id key, id value, BOOL *stop) {
NSLog (#"value for key %# is %#", key, value);
if ([#"ENOUGH" isEqualToString: key] || [value doubleValue] == stoppedValue) {
*stop = YES;
stoppedEarly = YES;
}
}];
1) it just makes sense for local variables to be strong, otherwise all objects would be released right after the creation or you would have to add the __strong attribute everytime yourself:
UIView *view = [[UIView alloc] init];
//if it wasn't a strong reference, view would be released here
//and you couldn't use the reference in the next line
view.backgroundColor = [UIColor blackColor];
2) the Block in your example gets called once for every key-value-pair in the dictionary. Since the intention of the code is to use one variable for all blockcalls, it is declared outside of it. Think of global variables and methods.
Edit: here are some basic examples on scopes of variables.
//global, visible in all methods
NSInteger globalVariable;
- (void)someMethod{
//local, visible only until the end of the method
NSInteger localMethodVariable;
void (^block)() = ^void() {
//local, visible only until the end of the block
NSInteger localBlockVariable;
//may use all 3 variables here
};
//may use globalVariable and localMethodVariable here
}
- (void)someOtherMethod{
//may use globalVariable here
}

I seem to be retaining self inside a block even when using an indirect __block reference to it, perhaps cause of copying the block to the heap?

[EDIT: Since it was causing confusion, this whole case assumes MRR and not ARC]
I'm having an odd (there's an explanation obviously, I just can't figure it out) behavior with a block which references self (indirectly) and is in turned copied to another object's property (that is, copied from object As' stack to the heap and retained by an object B). If the block doesn't contain the reference to _this, Object A's dealloc is called every time it is popped from a navigation controller, as it should. However, if the block references _this, the object's (MyObjectA in the code below) dealloc is only called every other time. That is, I push this view controller subclass, createBlock is called, I pop and nothing happens. I push again, createBlock is called again, then pop and then it DOES call dealloc on MyObjectA.
For the sake of brevity, I'm only posting the snippets I believe are key to the behavior.
Say I have an object MyObjectA (sublcass of a custom UIViewController), which includes a method createBlock, like so:
- (void)createBlock
{
__block MyObjectA* _this = self;
int(^animationBlock)(NSArray*,NSDictionary*);
animationBlock =
^(NSArray* layers, NSDictionary* parameters)
{
[CATransaction begin];
[CATransaction setCompletionBlock:
^{
for(CALayer* layer in layers)
layer.opacity = 1;
}];
CABasicAnimation* a2 = [CABasicAnimation animationWithKeyPath:#"opacity"];
a2.fromValue = [NSNumber numberWithFloat:0.];
a2.toValue = [NSNumber numberWithFloat:1.];
a2.duration = .4;
a2.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
a2.fillMode = kCAFillModeBoth;
a2.removedOnCompletion = NO;
CABasicAnimation* a = [CABasicAnimation animationWithKeyPath:#"position.x"];
a.duration = .4;
a.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
a.fillMode = kCAFillModeBoth;
a.removedOnCompletion = NO;
CAAnimationGroup* g = [CAAnimationGroup animation];
g.animations = [NSArray arrayWithObjects:a,a2, nil];
g.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
g.fillMode = kCAFillModeBoth;
g.removedOnCompletion = NO;
CALayer* numberLayer;
CALayer* flechaLayer;
CGFloat timeOffset = 0;
for(int i = 0; i < [layers count]; i+=2)
{
numberLayer = [layers objectAtIndex:i];
flechaLayer = [layers objectAtIndex:i+1];
a2.beginTime = [_this.view.layer convertTime:CACurrentMediaTime() fromLayer:nil] + timeOffset;
[numberLayer addAnimation:a2 forKey:nil];
a2.beginTime = 0;
a.fromValue = [NSNumber numberWithFloat:flechaLayer.frame.origin.x + 100];
a.toValue = [NSNumber numberWithFloat:flechaLayer.frame.origin.x + flechaLayer.frame.size.width / 2.];
g.duration = 3;
g.beginTime = [_this.view.layer convertTime:CACurrentMediaTime() fromLayer:nil] + timeOffset + .4;
[flechaLayer addAnimation:g forKey:nil];
timeOffset += 1.5;
}
[CATransaction commit];
return 0;
};
[[AnimationFactory sharedFactory] registerAnimationBlock:animationBlock forKey:#"EnsureFlechasNutricion"];
}
As you can see, there's a reference to _this in the animation block.
Then, the AnimationFactory's (a singleton) method which registers the blocks is:
- (void)registerAnimationBlock:(int(^)(NSArray*, NSDictionary*))animationBlock forKey:(NSString*)key
{
int(^heapBlock)(NSArray*, NSDictionary*) = [animationBlock copy];
[self.animationBlocks setObject:heapBlock forKey:key];
[heapBlock release];
}
My guess is that copying the block to the heap is retaining MyObjectA, or perhaps adding the block to the NSMutableDictionary in the AnimationFactory.. but I'm not sure.
Any thoughts?
Ok I figured it out: When I add the newly copied (heap) block to the AnimationFactory's dictionary, I'm necessarily retaining self even if doing the weak reference shebang when originally declaring the block in self.
The solution is to get a weak (aka __block Class* identifier = eval since I'm not on ARC) reference to self.view, which is the reason I was referencing self to start with, as opposed to one to self. This way, in spite of this view's reference count increasing, self's ref count stays correct. Then, on pop, self is not retained by the AnimationFactory's dictionary and does call dealloc.
I should mention that self's dealloc includes a call to another method which in turn removes all registered blocks from the AnimationFactory, bringing self.view's retain count back to normal too implicitly, and thus not leaking.
[Update: this answer applies when using ARC and from the comments it turns out MRC is being used, so its not the answer!]
The __block attribute is for when you need a variable which can be updated by a block, i.e. the variable is passed to the block by reference rather than by value as is the default case. This does not appear to be needed in your code, you don't update the value of _this within your block.
To break a strong reference cycle use the __weak attribute. Your current _this is a strong reference to the same object that self references, so your block ends up with a strong reference.

Regarding memory management in Objective C

According to the static analyzer if we have the following property:
#property (retain, nonatomic) SomeObject * object;
and then we assign the property like so:
self.object = [SomeObject alloc] init];
a leak occurs. This makes sense because the alloc init adds +1 to the retain count and then the retaining property also increments the retain count. What is the best solution here? typically I just add an autorelease like so:
self.object = [[SomeObject alloc] init] autorelease];
But sometimes this creates problems for me and I end up over releasing the object causing my app to crash. I don't have any specific examples right now but I remember I had to take out some autoreleases cause of the application crashing. Is there something I am missing here?
EDIT: I have a concrete example now of the issue I was running into.
NSMutableArray *newData = [NSMutableArray array];
//If this is true then we are showing all of the items in that level of hierarchy and do not need to show the summary button.
if (!(contextID.count >= 1 && [[contextID objectAtIndex:contextID.count - 1] isEqual:[NSNull null]]) && contextID.count != 0)
{
GeographyPickerItem * firstItem = [[GeographyPickerItem alloc] init];
firstItem.primaryString = [NSString stringWithString:#"Summary"];
firstItem.subString = [NSString stringWithString:#""];
firstItem.isSummaryItem = YES;
[newData addObject:firstItem];
[firstItem release]; //TODO: Figure out why this is causing EXC_BAD_ACCESS errors
}
self.hierData = newData;
The code above is in the init method of a viewcontroller. HierData is a retained property, which is released in the viewControllers dealloc method. GeographyPickerItem retains the two strings, primaryString and subString and releases them in its own dealloc method. My application crashes (sometimes) when the viewControllers are de-alloced following a pop off of a navigation controller. It crashes with a EXC_BAD_ACCESS signal in the dealloc method of GeographyPickerItem (either on [substring release] or [primaryString release]).
I don't understand why this is happening because I believe I am following proper memory management guidelines. If I comment out firstItem release everything is fine.
The autorelease method you mention is fine, as is the other common idiom of:
SomeObject *thing = [[SomeObject alloc] init];
self.object = thing;
[thing release];
If you end up overreleasing later on, that is your problem. This part, which you're apparently doing correctly, is not the problem.
SomeObject * new_object = [SomeObject alloc] init];
self.object = new_object;
[new_object release];
or use ARC
check the GeographyPickerItem, if the strings properties are assign (and change to retain), or check if you always initialize them (before release).
also remember the difference of manually allocating :
[[NSString alloc] initWith...]
You must release or autorelease.
[NSString stringWith...]
No need to release.
or use ARC like meggar said
Turns out the issue was simple, my dealloc method called super dealloc at the start of the method rather than at the end. You always have to release your instance variables before you call [super dealloc]!

Is myiVar = nil supported in ARC?

I'm using a code snippet like
if ([Array count] != 0) {
Array = nil;
}
Array = [[NSMutableArray alloc]init];
Is this allowed when using ARC? Does this cause any kind of crashes? Why I'm doing this is each time when my method gets called Array gets a new set of data. I'm using this kind of snippet in many places of my class.
ARC aside, the operation is pointless. You assign an ivar to nil then immediately assign to something else. This is no different from just assigning it to the something else.
Before ARC this would have given you a memory leak (with or without your assignment to nil) if there variable had a previous value. With ARC there is no leak.
Best solution : test and see by yourself ! This is a really short example !
But yes, this works of course !
ARC means Automatic Reference Counting. It just says you don't have to care about release, retain and so on.
If you want an object to be nil, you still can, as it is a simple pointer assignment !
And about your code, you set Array to nil before re-assigning it oO !
Try to get the logic of your code :
If my Array has objects
Then Array point to a new nil object
But in all case you do:
My Array point to a new NSMutableArray object
So whether or not your condition is evaluated to true, your code is useless as the variable will take another value just after !
The sample code you showed is valid. Here are a few variations:
if ([Array count] != 0) {
Array = [[NSMutableArray alloc] init];
}
Example 2:
if ([Array count] != 0) {
Array = [NSMutableArray array]; //value will be retained
}
Example 3:
if ([Array count] != 0) {
self.Array = [[NSMutableArray alloc] init]; //Will NOT leak under ARC.
}
Example 4:
//if ([Array count] != 0) {
[Array removeAllObjects];
//}
All four examples are valid under ARC.
Another thing: instance variables typically have their first letter lowercase, while still following the CamelCase method. Another way to do it is to have an underscore before the name of the variable: this is done if you don't have a #synthesize method to match your #property value.

Is NSMutableArray recursive in releasing it's items?

If I have a 2 dimensional NSMutableArray eg,
board = [[NSMutableArray alloc] initWithCapacity:boardHeight];
for (int y = 0; y < boardHeight; y++) {
NSMutableArray *row = [[NSMutableArray alloc] initWithCapacity:boardWidth];
for (int x = 0; x < boardWidth; x++) {
[row insertObject:#"A string"];
}
[board insertObject:row atIndex:y];
[row release];
}
and I do
[board release];
Does that recursively release the array? Or do I have to manually go into the array and release each row?
If it does, and the object inserted into each row were a custom object, is there anything special I have to think about when writing the dealloc method for the custom object?
Everything will just work fine. The array will retain the objects when they are added and released when removed or the array is deallocated.
No extra work on that part.
When board is ready to be deallocated, it will send the release message to each object in itself. Even if those objects are NSArrays, they will still be released; and if those arrays are now ready to be deallocated, they will send release to their members, etc.
Your class should implement dealloc normally -- release any ivars that you hold a strong reference to before calling [super dealloc].