Stuart Barth
Stuart Barth

Reputation: 305

NSMutableAray RemoveObject: Not Working As Expected

I have recently begun working with cocoa and Objective-C, although I am not new to software concepts, and I have come across some curious and unanticipated behavior in the NSMutableArray Class.

First, I create and populate a 'NSMutableArray' Object, with my custom objects of Class 'CustomClass' which works fine. Then I proceed to attempt to delete one of the objects, and I do not get the results I want.

// NSMutableArray *Array is already initialized and loaded with 5 CustomClass Objects
//   stored in variables 'CustomClassObject1' through 'CustomClassObject5'. 
[Array removeObject:CustomClassObject4];

Rather than this code simply removing the fourth entry, as I want it to, it sets the fourth entry to 'nil' (That is, (id) 0x00000000), and, more confusingly, deletes the last entry in the array.

In my understanding, this is not supposed to happen - all that should happen is the fourth entry should be removed, replaced by the fifth. Instead, I now have an array with three objects and one nil!

I've also tried

int position = [Array indexOfObject:CustomClassObject4];
[Array removeObjectatIndex:position];

Only to obtain the same result. Does anyone know why this is happening? Am I misunderstanding something?

Thanks!

Code:

NSMutableArray *Array = [NSMutableArray array];

CustomClass *CustomClassObject1 = [[CustomClass alloc] init];
CustomClass *CustomClassObject2 = [[CustomClass alloc] init];
CustomClass *CustomClassObject3 = [[CustomClass alloc] init];
CustomClass *CustomClassObject4 = [[CustomClass alloc] init];
CustomClass *CustomClassObject5 = [[CustomClass alloc] init];

[Array AddObject:CustomClassObject1];
[Array AddObject:CustomClassObject2];
[Array AddObject:CustomClassObject3];
[Array AddObject:CustomClassObject4];
[Array AddObject:CustomClassObject5];

Output at this point:

_Array = (NSMutableArray *) 0x0885c2c0 @ "5 Objects"
[0] = (CustomClass *) 0x0885c5f0
[1] = (CustomClass *) 0x0885c630
[2] = (CustomClass *) 0x0885c690
[3] = (CustomClass *) 0x0885c6f0
[4] = (CustomClass *) 0x0885c730

Then:

[Array removeObject:CustomClassObject4];

Output at this point:

_Array = (NSMutableArray *) 0x0885c2c0 @ "4 Objects"
[0] = (CustomClass *) 0x0885c5f0
[1] = (CustomClass *) 0x0885c630
[2] = (CustomClass *) 0x0885c690
[3] = (id) 0x00000000

If instead I delete the 3rd object,

[Array removeObject:CustomClassObject3];

Output is then:

_Array = (NSMutableArray *) 0x0885c2c0 @ "4 Objects"
[0] = (CustomClass *) 0x0885c5f0
[1] = (CustomClass *) 0x0885c630
[2] = (id) 0x00000000
[3] = (CustomClass *) 0x0885c6f0

Upvotes: 0

Views: 1057

Answers (2)

Ælex
Ælex

Reputation: 14869

Can you please try something simpler:

NSMutableArray *array = [NSMutableArray new];
[array addObject: [[CustomClass alloc] init] ];
// repeat for as many times you want.

// Fast enumerate the loop and get an output - You class will need to provide method id
for ( CustomClass * c in array ) NSLog( @"Class id: %@", [c id] );

// Then try to delete any random object.
[array removeObjectAtIndex: [array indexOfObject: object] ];

// And repeat the enumeration
for ( CustomClass * c in array ) NSLog( @"Class id: %@", [c id] );

Can you please try this and tell us what happened? Also, add breakpoints at each add and delete operation.

Make sure you have arc enabled, and that your code is within the autorelease pool!

Upvotes: 0

CodaFi
CodaFi

Reputation: 43330

It all boils down to how ARC handles local variables. For a moment, I'm going to pretend to turn off ARC to show you how your code is memory managed for illustration purposes:

NSMutableArray *Array = [NSMutableArray array];

//All of these are initialized with a +1 retain count
CustomClass *CustomClassObject1 = [[CustomClass alloc] init];
CustomClass *CustomClassObject2 = [[CustomClass alloc] init];
CustomClass *CustomClassObject3 = [[CustomClass alloc] init];
CustomClass *CustomClassObject4 = [[CustomClass alloc] init];
CustomClass *CustomClassObject5 = [[CustomClass alloc] init];

//The array retains what gets put into it, meaning it owns these variables
//And gives them a total reference count of +2
[Array AddObject:[CustomClassObject1 retain]];
[Array AddObject:[CustomClassObject2 retain]];
[Array AddObject:[CustomClassObject3 retain]];
[Array AddObject:[CustomClassObject4 retain]];
[Array AddObject:[CustomClassObject5 retain]];

//The compiler inserts a release for each of those objects because you
//Don't reference them before the method's scope ends.  The array still owns
//them, so they all have a total reference count of +1 at this point.
[CustomClassObject1 release];
[CustomClassObject2 release];
[CustomClassObject3 release];
[CustomClassObject4 release];
[CustomClassObject5 release];

When you remove an object, the array decrements it's reference count, so when you remove CustomClassObject4, it's reference count drops to a big fat 0, and it gets deallocated. That's the nil you're seeing. To fix it, retain those objects outside of the scope of that method in either strong iVars, properties, or a global.

Upvotes: 2

Related Questions