// AClass.m
// init
enemyBullets = [[NSMutableArray alloc] initWithCapacity:0];
enemy1 = [[Enemy alloc] initWithBullets:enemyBullets];
// At some point
NSMutableArray *bulletsToDelete = [NSMutableArray array];
for(BulletEnemy *thisBullet in enemyBullets)
{
// If I have to delete
[bulletsToDelete addObject: thisBullet];
}
[enemyBullets removeObjectsInArray:bulletsToDelete];
//dealloc method
[enemyBullets release];
[enemy1 release];
Now Inside Enemy some point in time I do the following:
// Enemy.m
- (id)initWithBullets:(NSMutableArray*) _bullets{
// Enemybullets is a var of Enemy
enemyBullets = _bullets;
}
// At some point...
myBullet = [[BulletEnemy alloc] init];
[enemyBullets addObject:myBullet];
[myBullet release];
The problem is when I do the following at Aclass:
[enemyBullets removeObjectsInArray:bulletsToDelete];
The dealloc method inside BulletEnemy doesn't get called because the retain count isn't 0. Why? But If I release ACLass (which releases enemyBullets) then My bullets get deallocated.
Make Enemy own the enemyBullets. And use reverseObjectEnumerator
// AClass.m
// init
enemy1 = [[Enemy alloc] init];
// At some point
for(BulletEnemy *thisBullet in enemy1.enemyBullets.reverseObjectEnumerator)
{
// If I have to delete
[enemy1.enemyBullets removeObject:thisBullet];
}
//dealloc method
[enemy1 release];
//Enemy.h
#property (retain) NSMutableArray* enemyBullets;
// Enemy.m
#synthesize enemyBullets = _enemyBullets;
- (id)init{
// Enemybullets is a var of Enemy
_enemyBullets = [[NSMutableArray alloc] init];
}
// At some point...
myBullet = [[BulletEnemy alloc] init];
[enemyBullets addObject:myBullet];
[myBullet release];
-(void) dealloc{
[_enemyBullets release];
}
Aparently, I was asigning something with retain propery, which made the object +1 thus not to deallocate. Just changed the propery to assign and it worked.
Related
This question already has answers here:
Cannot add items to an NSMutableArray ivar
(4 answers)
Closed 8 years ago.
I'm trying to add NSMutableArray to other NSMutableArray, a mutable array set as instance ivar, but I allways got the array arrayPlayoff empty. Other thing that I have detected, is that when debugging in the attached method, local variables are not shown in the debugger variables section, even selecting "local" option.
-(void)loadGamesPlayoffs{
NSMutableArray *eli1 = [[NSMutableArray alloc] init];
NSMutableArray *eli2 =[[NSMutableArray alloc] init];
NSMutableArray *eli3 = [[NSMutableArray alloc] init];
NSMutableArray *eli4 = [[NSMutableArray alloc] init];
for (NSDictionary *eliminatoria in copaReyArray){
int eli = [[eliminatoria valueForKey:#"eliminatoria"]integerValue];
NSLog(#"eli %d", eli);
if (eli==1){
[eli1 addObject:eliminatoria];
} else if (eli==2){
[eli2 addObject:eliminatoria];
}else if (eli==3){
[eli3 addObject:eliminatoria];
} else if (eli==4){
[eli4 addObject:eliminatoria];
}
}
[arrayPlayOff addObject:eli1];
[arrayPlayOff addObject:eli2];
[arrayPlayOff addObject:eli3];
[arrayPlayOff addObject:eli4];
}
many thanks
If arrayPlayOff is an Instance Variable you could use properties, your .h should look something like this:
#import <UIKit/UIKit.h>
#interface YourClassViewController : UIViewController{
NSMutableArray *arrayPlayOff;
//Some other variables
}
#property (nonatomic, retain) NSMutableArray *arrayPlayOff;
//Some other methods
#end
Then in your .m file you could use:
#implementation YourClassViewController
#synthesize arrayPlayOff;
- (NSMutableArray *)arrayPlayOff{
if(!arrayPlayOff){
arrayPlayOff = [[NSMutableArray alloc] init];
}
return arrayPlayOff;
}
//Your other methods
//Overwrite the dealloc function so you don't have any memory leaks
- (void)dealloc{
[arrayPlayOff release];
[super dealloc];
}
#end
Now you could access your variable as
self.arrayPlayOff
and this way you can be 100% sure that your variable is always initialized.
Your method could look like this (Also you should release your other NSMutableArrays since you are not using after adding them to the arrayPlayOff array):
-(void)loadGamesPlayoffs{
NSMutableArray *eli1 = [[NSMutableArray alloc] init];
NSMutableArray *eli2 =[[NSMutableArray alloc] init];
NSMutableArray *eli3 = [[NSMutableArray alloc] init];
NSMutableArray *eli4 = [[NSMutableArray alloc] init];
for (NSDictionary *eliminatoria in copaReyArray){
int eli = [[eliminatoria valueForKey:#"eliminatoria"]integerValue];
NSLog(#"eli %d", eli);
if (eli==1){
[eli1 addObject:eliminatoria];
} else if (eli==2){
[eli2 addObject:eliminatoria];
}else if (eli==3){
[eli3 addObject:eliminatoria];
} else if (eli==4){
[eli4 addObject:eliminatoria];
}
}
[self.arrayPlayOff addObject:eli1];
[self.arrayPlayOff addObject:eli2];
[self.arrayPlayOff addObject:eli3];
[self.arrayPlayOff addObject:eli4];
//Releasing the NSMutableArrays
[eli1 release];
[eli2 release];
[eli3 release];
[eli4 release];
}
Although experienced with OOP, I am an absolute newbie with Objective-C. I have the following code:
// header files have been imported before this statement...
CCSprite *treeObstacle;
NSMutableArray *treeObstacles;
#implementation HelloWorldLayer {
}
-(id) init
{
// create and initialize our seeker sprite, and add it to this layer
treeObstacles = [NSMutableArray arrayWithObjects: nil];
for (int i=0; i<5; i++) {
treeObstacle = [CCSprite spriteWithFile: #"Icon.png"];
treeObstacle.position = ccp( 450-i*20, 100+i*20 );
[self addChild:treeObstacle];
[treeObstacles addObject: treeObstacle];
}
NSLog (#"Number of elements in array = %i", [treeObstacles count]);
return self;
}
- (void) mymethod:(int)i {
NSLog (#"Number of elements in array = %i", [treeObstacles count]);
}
#end
The first NSLog() statement returns "Number of elements in array = 5". The problem is that (although treeObstacles is a file-scope variable) when calling the method "mymethod", I'll get an EXC_BAD_ACCESS exception.
Can anybody please help me?
Thanks a lot
Christian
you created treeObstacles by
treeObstacles = [NSMutableArray arrayWithObjects: nil];
which will return an autoreleased object, and you didn't retain it so it will be released soon
you have to retain it by calling retain on it
[treeObstacles retain];
of simple create it by
treeObstacles = [[NSMutableArray alloc] init];
and you need to remember to release it when done like
- (void)dealloc {
[treeObstacles release];
[super dealloc];
}
you need to read more about management in Objective-C
https://developer.apple.com/library/mac/#documentation/General/Conceptual/DevPedia-CocoaCore/MemoryManagement.html
or use ARC so no need to worry retain/release anymore
http://developer.apple.com/library/ios/#releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html
another problem, you need to call [super init] in your init method
- (id)init {
self = [super init];
if (self) {
// your initialize code
}
}
otherwise your object won't initialize properly
i have a property
//.h
#property (nonatomic, retain) SomeCLass * someSynthInstance;
//.m
#synthesize someSynthInstance = _someSynthInstance;
- (void) foo
{
self.someSynthInstance = [[SomeCLass alloc] init];
//[self.someSynthInstance release]; <- SHOULD I RELEASE HERE???
}
- (void) dealloc
{
self.someSynthInstance = nil;
}
my theory goes, that [alloc, init] creates a count of 1, and the setter, inc that count, so it becomes 2, therefore i should release it, right after
but aim getting exc_bad_access in the application after i changed everything like this, so aim not sure if its ok
you want to be releasing the instance variable rather than the property. so you can do either:
self.someSynthInstance = [[[SomeCLass alloc] init] autorelease]; // puts it in the autoreleasepool so it'll get released automatically at some point in the near future
or
_someSynthInstance = [[SomeCLass alloc] init]; // skip the property
or
self.someSynthInstance = [[SomeCLass alloc] init];
[_someSynthInstance release]; // call release on the instance variable
The standard practice is:
SomeClass *tmpObj = [[SomeClass alloc] init];
self.someSynthInstance = tmpObj;
[tmpObj release];
you should release the class variable instead.. like [_someSynthInstance release]; that should do the trick.
yes you should release after init
SomeCLass *temp = [[SomeCLass alloc] init];
self.someSynthInstance = temp;
[temp release]
I just ran my app through the Leaks in Instruments and I am being told that the following code causes leaks, but I don't see how.
I allocate some NSMutableArrays in my viewDidLoad with this code:
- (void)viewDidLoad {
[super viewDidLoad];
self.currentCars = [[NSMutableArray alloc] init];
self.expiredCars = [[NSMutableArray alloc] init];
}
Then I populate these arrays inside of my viewWillAppear method with the following:
[self.currentCars removeAllObjects];
[self.expiredCars removeAllObjects];
for (Car *car in [self.dealership cars]) {
if ([car isCurrent])
[self.currentCars addObject:car];
if ([car isExpired])
[self.expiredCars addObject:car];
}
And later in the code I release these arrays here:
- (void) viewWillDisappear:(BOOL)animated {
if (currentCars != nil) {
[currentCars release], currentCars = nil;
}
if (expiredCars != nil) {
[expiredCars release], expiredCars = nil;
}
[super viewWillDisappear:animated];
}
Any ideas? Thanks!
Your leak is here:
self.currentCars = [[NSMutableArray alloc] init];
self.expiredCars = [[NSMutableArray alloc] init];
Assuming that you declared property accessores like this:
#property(nonatomic, retain) NSMutableArray *currentCars;
#property(nonatomic, retain) NSMutableArray *expiredCars;
In my opinion, the best way to find leaks (other than using Instruments) is to keep track of the retain count manually.
If you were to do that with for example currentCars, you would find your leak easily. Here is what happens:
self.currentCars = [[NSMutableArray alloc] init];
// The 'init' makes the retain count 1.
// 'self.currentCars = ..' translates to the setCurrentCars: method.
// You probably did not implement that method yourself,
// but by synthesizing your property it is automatically implemented like this:
- (void)setCurrentCars:(NSMutableArray *)array {
[array retain]; // Makes the retain count 2
[currentCars release];
currentCars = array;
}
// In your viewWillDisappear: method
[currentCars release], currentCars = nil; // Makes the retain count 1 so the object is leaked.
The solution is simple. Use this:
NSMutableArray *tempMutableArray = [[NSMutableArray alloc] init];
self.currentCars = tempMutableArray;
[tempMutableArray release];
A little sidenote. You shouldn't release your objects in viewWillDisappear:. The recommended place to do that is dealloc. So your code would be:
- (void)dealloc {
[currentCars release], currentCars = nil;
[expiredCars release], expiredCars = nil;
[super dealloc];
}
The problem is (probably) that you are using the property accessors for the initial setting of the arrays in -viewDidLoad. Since well-implemented property accessors will retain the object, you are getting 1 retain from the +alloc and another retain from assigning it. To fix this, you should release your arrays after assigning them or use [NSMutableArray array] to get an autoreleased one to use for your initial assignments.
Unless you're doing something very odd in currentCars, expiredCars, dealership or cars, no, there's no leak there.
Instruments' pointer to the location of a leak isn't necessarily where the object is actually leaked, per se. If I were to guess, I'd say you're probably neglecting to release either currentCars or expiredCars in your dealloc method.
Given a basic key/value array, I'm wanting to store two sorted arrays based on the original array: one array will be sorted by name, and the other by age.
The arrays seem to be sorting correctly when I output them to the log; however, when I try to access them elsewhere in the code, I'm receiving a EXC_BAD_ACCESS error.
Here's what I have so far:
// MyController.h
#interface MyController : UIViewController {
NSMutableArray *originalArray;
NSMutableArray *nameArray;
NSMutableArray *ageArray;
}
#property (nonatomic, retain) NSMutableArray *originalArray;
#property (nonatomic, retain) NSMutableArray *nameArray;
#property (nonatomic, retain) NSMutableArray *ageArray;
-(void)someRandomMethod;
#end
// MyController.m
#import "MyController.h"
#implementation MyController
#synthesize originalArray;
#synthesize nameArray;
#synthesize ageArray;
-(void)viewDidLoad {
// originalArray = (
// {
// "name" = "Sally";
// "age" = 18;
// },
// {
// "name" = "Chad";
// "age" = 26;
// },
// {
// "name" = "Carla";
// "age" = 24;
// },
// )
// sort by name
NSSortDescriptor *sortByNameDescriptor;
sortByNameDescriptor = [[[NSSortDescriptor alloc]
initWithKey:#"name"
ascending:NO] autorelease];
NSArray *sortByNameDescriptors = [NSArray arrayWithObject:sortByNameDescriptor];
nameArray = [originalArray sortedArrayUsingDescriptors:sortByNameDescriptors];
// sort by age
NSSortDescriptor *sortByAgeDescriptor;
sortByAgeDescriptor = [[[NSSortDescriptor alloc]
initWithKey:#"age"
ascending:NO] autorelease];
NSArray *sortAgeDescriptors = [NSArray arrayWithObject:sortByAgeDescriptor];
ageArray = [originalArray sortedArrayUsingDescriptors:sortByAgeDescriptors];
[super viewDidLoad];
}
-(void)someRandomMethod {
// whenever I try to access the sorted arrays, I receive the EXC_BAD_ACCESS error
[[nameArray objectAtIndex:0] valueForKey:#"name"];
[[ageArray objectAtIndex:0] valueForKey:#"age"];
}
-(void)viewDidUnload {
self.originalArray = nil;
self.nameArray = nil;
self.ageArray = nil;
[super viewDidUnload];
}
- (void)dealloc {
[originalArray release];
[nameArray release];
[ageArray release];
[super dealloc];
}
#end
Any ideas?
UPDATE: Thanks to #robin, by changing the code above to the code below, everything works great:
// sort by name
NSSortDescriptor *sortByNameDescriptor;
sortByNameDescriptor = [[[NSSortDescriptor alloc]
initWithKey:#"name"
ascending:NO] autorelease];
NSArray *sortByNameDescriptors = [NSArray arrayWithObject:sortByNameDescriptor];
nameArray = [[NSMutableArray alloc] initWithArray:[originalArray sortedArrayUsingDescriptors:sortByNameDescriptors]];
// sort by age
NSSortDescriptor *sortByAgeDescriptor;
sortByAgeDescriptor = [[[NSSortDescriptor alloc]
initWithKey:#"age"
ascending:NO] autorelease];
NSArray *sortAgeDescriptors = [NSArray arrayWithObject:sortByAgeDescriptor];
ageArray = [[NSMutableArray alloc] initWithArray:[originalArray sortedArrayUsingDescriptors:sortByAgeDescriptors]];
I dont think you know about this or not but when ever you create an object like string or array or dictionary, with init methods then the retain count gets incremented by 1
and if you create them like this
NSArray *anarray = [NSArray arrayWithArray:temp];
this will create an autorelease objects that will be released automatically after sometime.
So my advice don't use this type of code if you want to use the objects in more than 1 function. Always use init methods first to get the work done.
and if you are sure that the objects are not needed for the rest of the program than release them using release methode.