Correctly removing objects from view and array? - iphone

In my "connect4" style game I have an array representing a 7x6 grid, each "cell" in the array contains either NSNull or a UIView subclass 'CoinView'. Is the following the correct way to remove objects from the NSMutableArray and the primary view?
- (IBAction)debugOrigin:(id)sender {
int x = 0;
int y = 0;
//get the coin object form the grid
CoinView *coin = [[grid objectAtIndex:x] objectAtIndex:y];
//cancel if there's no coin there
if ([coin isKindOfClass:[NSNull class]]) { return; }
//remove the coin from memory
[coin removeFromSuperview];
coin = nil;
[[grid objectAtIndex:x] setObject:[NSNull null] atIndex:y]; //will this leak?
}
Thanks!

Your code will not leak, and is in fact (almost) correct.
You should remove this comment, as you're not dealing with memory in your code (and it may end up confusing you as to what the code really does):
//remove the coin from memory
In the following line you're removing the view referenced by local variable "coin" from its superview:
[coin removeFromSuperview];
And you assign nil to your local variable coin, which is good practice to make sure it's not being used later in the code:
coin = nil;
As far as I know there is no setObject:AtIndex: for NSMutableArray. Use replaceObjectAtIndex:withObject: instead:
[[grid objectAtIndex:x] replaceObjectAtIndex:y withObject:[NSNull null]]; //will this leak?
As a last note, I recommend that you read up a bit on memory management and memory leaks (from Apple's developers documentation). The first offers you some hints and tips that make memory management much easier to understand.

Related

sometimes crash EXC_BAD_ACCESS with no message (set NSZombieEnabled)

I'm handling RemoteIO to get mic inputs and modify them little.
Sometimes it crashes with EXC_BAD_ACCESS and there is no more message.
The lines that make crashes are these;
int currPower = [[powers objectAtIndex:i] intValue];
int prevPower = [[powers objectAtIndex:i - 1] intValue];
explaining the code,
"powers" is NSMutableArray.
[powers count] was always bigger than variable "i"
Struggling for a while, I found a good way to fix it.
A environment variables.
So I set NSZombieEnabled and also NSDebugEnabled so that I could see the reason of the crashes.
But even though I set the variables, Xcode shows no message.
(But it correctly shows messages when a crash occurs from other line.)
Also a weird thing is that it doesn't crash just after the start of run;
it crashes in a minute in average. (But the times really varied.)
And this is a little guess. When I decreased the rate to half than before,
it was more stable.
So, is it a problem with NSMutableArray, because NSMutableArray method couldn't catch up the speed of the rate?
or do you see some other possible reasons?
=========================================================================================
There are some more codes.
I allocated powers in this way..
powers = [[NSMutableArray alloc] initWithCapacity:POWER_ARRAY_SIZE];
where I release the powers array is..
- (void)dealloc {
[powers release];
[super dealloc];
}
and no where else.
more detailed code is this.
- (void)drawRect:(CGRect)rect
{
...//Do Something
...//Check "endindex" and "startindex" not to exceed boundary
for (int i = endindex; i > startindex; i-=1)
{
int currPower = [[powers objectAtIndex:i] intValue];
int prevPower = [[powers objectAtIndex:i - 1] intValue];
...//Doing something
}
}
this drawRect: method is calling from Main Thread(By Timer) in every millisecond.
--
updating(more specifically adding) powers in this method
- (void)setPower:(int)p
{
[powers addObject:[NSNumber numberWithInt:p]];
while ([powers count] > POWER_ARRAY_SIZE){
[powers removeObjectAtIndex:0];
}
}
and also this method is calling in every millisecond.
and this is calling in background thread.
so without #autoreleasepool XCode Shows message of alert of leaking
for this reason I blocked the method(setPower) with #autoreleasepool{..}
If NSZombies solved your problem it means that your NSMutableArray is being released somewhere, or it's in an autorelease pool. Also you could be trying to write outside the bounds of your array.
If the NSMutableArray is in an autorelease pool (it was created by a convenience method or you explicitly autoreleased it) manually retain it and release it when you no longer need it.
If the object is not in an autorelease pool check when the release is called for that object.
Write a simple warning before the assignment:
if( i <= 0 || i >= [powers count] ) NSLog(#"Here's the problem. i = %d", i);
I found the answer.
Crashes are occurred because NSMutableArray of objected-C sometimes ack wrong.
That's when I try to do something in every milliseconds with it.
So I changed the Objective-C array to C array, like
int power[ARRAYSIZE];
and after I changed it, it works fine.
May be NSMutableArray isn't that light to do something really fast.

How to release an object in a forin loop?

I'm new to cocoa / objective-c and i'm struggeling with the releases of my objects. I have the following code:
gastroCategoryList = [[NSMutableArray alloc] init];
for (NSDictionary *gastrocategory in gastrocategories) {
NSString *oid = [gastrocategory objectForKey:#"id"];
GastroCategory *gc = [[GastroCategory alloc] initWithId:[oid intValue] name:[gastrocategory objectForKey:#"name"]];
[gastroCategoryList addObject:gc];
}
The analyzer shows me that the "gastrocategory" defined in the for is a potential memory leak. But i'm not sure if i can release this at the end of the for loop?
Also at the following code:
- (NSArray *)eventsForStage:(int)stageId {
NSMutableArray *result = [[NSMutableArray alloc] init];
for (Event *e in eventList) {
if ([e stageId] == stageId) {
[result addObject:e];
}
}
return result;
}
The Analyzer tells me that my "result" is a potential leak. But where should I release this?
Is there also a simple rule to memorize when i should use assign, copy, retain etc. at the #property ?
Another problem:
- (IBAction)showHungryView:(id)sender {
GastroCategoriesView *gastroCategoriesView = [[GastroCategoriesView alloc] initWithNibName:#"GastroCategoriesView" bundle:nil];
[gastroCategoriesView setDataManager:dataManager];
UIView *currentView = [self view];
UIView *window = [currentView superview];
UIView *gastroView = [gastroCategoriesView view];
[window addSubview:gastroView];
CGRect pageFrame = currentView.frame;
CGFloat pageWidth = pageFrame.size.width;
gastroView.frame = CGRectOffset(pageFrame,pageWidth,0);
[UIView beginAnimations:nil context:NULL];
currentView.frame = CGRectOffset(pageFrame,-pageWidth,0);
gastroView.frame = pageFrame;
[UIView commitAnimations];
//[gastroCategoriesView release];
}
I don't get it, the "gastroCategoriesView" is a potential leak. I tried to release it at the end or with autorelease but neither works fine. Everytime I call the method my app is terminating. Thank you very much again!
In your loop, release each gc after adding it to the list since you won't need it in your loop scope anymore:
gastroCategoryList = [[NSMutableArray alloc] init];
for (NSDictionary *gastrocategory in gastrocategories) {
NSString *oid = [gastrocategory objectForKey:#"id"];
GastroCategory *gc = [[GastroCategory alloc] initWithId:[oid intValue] name:[gastrocategory objectForKey:#"name"]];
[gastroCategoryList addObject:gc];
[gc release];
}
In your method, declare result to be autoreleased to absolve ownership of it from your method:
NSMutableArray *result = [[[NSMutableArray alloc] init] autorelease];
// An alternative to the above, produces an empty autoreleased array
NSMutableArray *result = [NSMutableArray array];
EDIT: in your third issue, you can't release your view controller because its view is being used by the window. Setting it to autorelease also causes the same fate, only delayed.
You'll have to retain your GastroCategoriesView controller somewhere, e.g. in an instance variable of your app delegate.
BoltClock's answer is spot-on as to the first part of your question. I'll try to tackle the rest.
Assign is for simple, non-object types such as int, double, or struct. It generates a setter that does a plain old assignment, as in "foo = newFoo". Copy & retain will, as their names imply, either make a copy of the new value ("foo = [newFoo copy]") or retain it ("foo = [newFoo retain]"). In both cases, the setter will release the old value as appropriate.
So the question is, when to copy and when to retain. The answer is... it depends. How does your class use the new value? Will your class break if some other code modifies the incoming object? Say, for example, you have an NSString* property imaginatively named "theString." Other code can assign an NSMutableString instance to theString - that's legal, because it's an NSString subclass. But that other code might also keep its own reference to the mutable string object, and change its value - is your code prepared to deal with that possibility? If not, it should make its own copy, which the other code can't change.
On the other hand, if your own code makes no assumptions about whether theString might have been changed, and works just as well whether or not it was, then you'd save memory by retaining the incoming object instead of unnecessarily making a copy of it.
Basically, the rule, which is unfortunately not so simple sometimes, is to think carefully about whether your own code needs its own private copy, or can correctly deal with a shared object whose value might be changed by other code.
The reason you can release gc after it is added to the gastroCategoryList is that when an object is added to an array, the array retains that object. So, even though you release your gc, it will still be around; retained by the gastroCategoryList.
When you are returning a newly created object from a method, you need to call autorelease. This will cause the object to be released only after the runtime leaves the scope of the calling method, thereby giving the calling method a chance to do something with the returned value.
Note that if your method starts with the word copy or new, then you should not autorelease your object; you should leave it for the calling method to release.
As for copy vs retain vs assign... as a general rule, copy objects that have a mutable version, such as NSArray, NSSet, NSDictionary, and NSString. This will ensure that the object you have a pointer to is not mutable when you don't want it to be.
Otherwise, use retain whenever you want your class to be ensured that an object is still in memory. This will apply to almost every object except for objects that are considered parents of your object, in which case you would use assign. (See the section on retain cycles here).
Also note that you have to use assign for non-object types such as int.
Read through the Memory Management Programming Guide a bit; it's quite helpful.

NSMutableArray and memory dealloc

Im making an app for the iphone using cocos2d and i am trying to figure out the best approach for removing items from a NSmutableArray and from the layer at the same time.
What i mean by this is that the objects within the array inherit from ccNode and contain a ccsprite which i have added as a child to the cclayer. The below code is in a cclayer that has the nsmutablearray called bonusicons.
-(void) AddNewBonusIcon: (int) colour :(int) pos{
BonusIcon *newbonus;
CGSize winSize = [[CCDirector sharedDirector] winSize];
int maxX = winSize.width;
int maxY = winSize.height;
int posX, posY;
newbonus = [[BonusIcon alloc] init];
[newbonus setBonusColour: colour];
int bonusOffset = 0;
posX = anchorX;
posY = anchorY;
bonusOffset = [bonusIcons count]*([newbonus.bonus_sprite boundingBox].size.width/2 + 12);
newbonus.bonus_sprite.position = ccp(posX+bonusOffset,posY);
[newbonus.bonus_sprite setTag:pos];
[self addChild:newbonus.bonus_sprite];
[bonusIcons addObject:newbonus ];
[newbonus release];
}
This appears to do what i want for adding the objects sprite to screen and adding the objects to the nsmutablearray. Now of course this is probably not the correct way to do it so shout at me if not!
next i try to delete the objects from the array and from the screen. I can delete them from the array with no problems i just do the following
for (int i = INITIAL_BONUSES-1; i>=0; i--) {
[bonusIcons removeObjectAtIndex:i];
}
this of course leaves the sprites on screen. so how do i approach what i am trying to do so that i can remove both the sprites from screen and the objects from array that the sprite is associated with. I can remove the sprites from the screen by using the tags and typing
[self removeChildByTag:i cleanup:YES]; but then i get errors when trying to remove items from the array . i assume because i have deleted a part of the object already and the dealloc of the ccnode can no longer find the sprite to release?
so any pointers/tips etc of how i should be doing this would be much appreciated. I have read a bunch of stuff on memory management which i believe is my current issue but i just dont seem to be getting it right.
thanks all
edit: ok since posting this i have removed the sprite dealloc from the ccnode itself and added it to the cclayer above it. This has stopped the crashing so i guess i was right with the problem i was having. I of course do not think the way i solved it is the most ideal way but it will do until i find a better way.
You don't have it in the code you posted, but your question seems to strongly imply that you are calling dealloc. The only place you should ever call dealloc is [super dealloc] at the end of a class's dealloc method. Calling it on anything but super or in any other place is wrong and will lead to errors about prematurely deallocated objects (because, well, that's what it does).
If this is what you're doing, I strongly suggest you read Apple's memory management guide. It lays out how memory management works in Cocoa very simply yet thoroughly.

Convert a string + a number to an onject

I am new to Obj C, but familiar with OOP. I am working on a game that uses collision detection to score points and change UIImageView properties like background color.
I have a finite number of UIImageViews that are subviews of a larger view. I can manually check for collision and set the background colors of the UIImageViews accordingly. The problem is that this creates a rather large if / else block that I can see growing out of control. In JavaScript I can use a for loop and eval() to evaluate a string + i (the loop var) and then simply call the new var.backgroundColor = someColor;
It seems that eval is not used in OBJ C, so I am looking for an equivalent that will allow me to create a loop that executes the same block of code on a given object that only differs from other objects by name only:
Example:
if (someStatement == true){
object_01.someProperty = newProperty;
} else if (someOtherStatement == true){
object_02.someProperty = newProperty;
} else if (someOtherOtherStatement == true){
object_03.someProperty = newProperty;
}
I want to write the above as a loop. I realize I can use an Array, but if so, how? Also, I want to loop through CGRects which are not storing properly in an Array.
Any help greatly appreciated!
Damon
Creating your array:
NSMutableArray *items = [NSMutableArray array];
Populating your array with CGRects:
[items addObject:[NSValue valueWithCGRect:frame]];
Pulling a CGRect from the array:
CGRect frame = [[items objectAtIndex:0] CGRectValue];
Replacing an index in the array:
[items replaceObjectAtIndex:0 withObject:[NSValue valueWithCGRect:frame]];
The best way is to put object_01, object_02, ... into an array and then use `-[NSArray objectAtIndex:] to access the objects.
You could also use Key-Value Coding:
NSString *keyPath = [NSString stringWithFormat:#"object_0%d.someProperty", objectIndex];
[self setValue:newProperty forKeyPath:keyPath];
But as rpetrich mentioned, it's not good programming style.

Problem adding multiple annotations to map

Ok, so I’m having this problem. What I want to do is manually add multiple annotations to a map. When I add just one annotation, it works flawlessly. The pin drops, you can click on it to see its callout, life is good.
The problem comes when I want to add more than one. When I add the second, suddenly the pin’s aren’t coloured correctly (i.e. depending on their magnitude they should be a certain color, but they’re now both the same…), and more importantly when you click on them, to see their callout, the app crashes with exex_bad_access. I really have no idea what’s wrong, maybe I’m adding too many views to the map? But it’s only 9 pins and the pins themselves add just fine.
Here’s my code…
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableArray *stops = [[NSMutableArray alloc] init]; //Get list of all the stops available
Bus *bus1 = [[Bus alloc] init]; // Bus 1 holds the stops
stops = [bus1 returnStops];
for (NSString *stop in stops) //Go through each stop to add annotation to map
{
Bus *bus2 = [bus1 initWithStop:stop]; //Create an instance of bus with a given stop
MapAnnotation *eqAnn = [MapAnnotation annotationWithBus:bus2];
[self.mapView addAnnotation:eqAnn]; //Add the annotation to the map
//[eqAnn release];
//[bus2 release];
}
[self recenterMap];
[stops release];
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView
viewForAnnotation:(id <MKAnnotation>)annotation {
MKAnnotationView *view = nil;
if(annotation != mapView.userLocation) {
MapAnnotation *eqAnn = (MapAnnotation*)annotation;
view = [self.mapView dequeueReusableAnnotationViewWithIdentifier:#"busLoc"];
if(nil == view) {
view = [[[MKPinAnnotationView alloc] initWithAnnotation:eqAnn
reuseIdentifier:#"busLoc"] autorelease];
}
CGFloat magnituide = [eqAnn.bus.magnitude floatValue];
if(magnituide >= .80f) {
[(MKPinAnnotationView *)view setPinColor:MKPinAnnotationColorRed];
} else if(magnituide >= .60f) {
[(MKPinAnnotationView *)view setPinColor:MKPinAnnotationColorPurple];
} else
{
[(MKPinAnnotationView *)view setPinColor:MKPinAnnotationColorGreen];
}
[(MKPinAnnotationView *)view setAnimatesDrop:YES];
[view setCanShowCallout:YES];
}
return view;
}
even tried removing the second function, but it didn’t do anything.
Thanks for the help!
P.S I should also add, there’s usually one or two pins out of the 9 which works when you click the annotation…
If i even try to manually just two annotations by hand in the program (i.e., remove the loop), it still fails and the color is still wrong...
It would appear that your memory management of the stops variable is incorrect. You allocate a mutable array, then replace that array with the return value of -[Bus returnStops], then release that. Also it's not clear what's going on with bus2 - does -[Bus initWithStop:] return a different instance of Bus? It's not usual to send any method -init* on an already-initialised object. I think that you probably are confused by the memory management conventions in Cocoa Touch. Here's a collection of articles and other references on Cocoa memory management (which is the same beast).
Have you tried using AddAnnotations instead of add annotation? - (void)addAnnotations:(NSArray *)annotations. This might work for you...but looking at the answer above and further inspection you are having some memory managment issues in your viewDidLoad (though thi s might not be the cause of your problem, but it could be). First of you are allocating the array (stops) and then ovveriding it with some array in the Bus object, this will cause a leak. Also you are then releasing that array which might be causing the crash since you are releasing the array that is actually in the Bus object w ithout having increased a reference count to it. I am not sure what initWithStop is doing but you might be getting a leak here too if initWithStop retains the object.
I wouldn't call it a memory management problem -- I'd just say you are using array references incorrectly.
After constructing the array with NSMutableArray *stops = [[NSMutableArray alloc] init], the next step is to use [stops addObject: ] to add each stop you want to store.
After that? It's not clear what you are really trying to do.
SO the answer was that I kept sending bus1 the init object, so it got confused.
"Hi David,
Your data model looks hosed to me. You only have one bus object that you are repeatedly sending initWithStop: to.
Hope this helps.
Good luck!
"
Thank you guys for your help! You all helped me quite a bit!