views:

799

answers:

3

I have tried to initialize my NSMutableArray 100 ways from Sunday, and NOTHING is working for me. I tried setting it equal to a newly allocated and initialized NSMutableArray, just allocating, initializing the variable by itself, every combination I could think of and always the same result.

Here's the code:

Object.h

NSMutableArray *array;

@property (copy) NSMutableArray *array;

Object.m

@synthesize array;

if ( self.array ) {
    [self.array addObject:anObject];
}
else {
    self.array = [NSMutableArray arrayWithObjects:anObject, nil];
}

NOTE: In debug "anObject" is NOT nil at time of execution...

I have tested anObject and it isThe initialization works just fine, but I keep getting the error below when I try to addObject: to self.array.

2010-07-10 11:52:55.499 MyApp[4347:1807] -[__NSArrayI addObject:]: unrecognized selector sent to instance 0x184480

2010-07-10 11:52:55.508 MyApp[4347:1807] * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayI addObject:]: unrecognized selector sent to instance 0x184480'

Does anyone have any idea what's going wrong?

Thanks, Z@K!

+3  A: 

The synthesized setter for @property (copy) sends a copy message to the array, which results in an immutable copy.

You have no choice but the implement the setter yourself here, as detailed in the Objective-C guide.

Georg Fritzsche
ABSOLUTELY CORRECT! I realized it as I was writing my post, and I posted my resolution below. I didn't really consider overriding the setter, but in my case I don't really need a copy so my solution worked out for me. Thanks for the quick reply! - Z@K!
Zak
A: 

As I was proof reading my post, a thought occurred to me and I answered my own question. This resolution was obscure enough that I decided to go ahead, create the post and answer it myself (so any other newbies, like myself, won't get hung up).

My mistake was in...

@property (copy) NSMutableArray *array;

it should have been...

@property (retain) NSMutableArray *array;

The error was not happening in the way I was executing my code, but rather in the way the anObject was attempting to "copy" the NSMutableArray array.

As we all know...

mutableArray = [mutableArray copy];

is not always (or ever, in my experience) equal to...

mutableArray = [mutableArray mutableCopy];

And this was the source of my problem. By simply switching the @property from (copy) to (retain) I solved my problem.

I hope this helps, Z@K!

Zak
A: 

I would like to tip my hat to Georg Fritzsche. I did end up needing to use (copy) instead of (retain), and I would not have known what to do without his input.

//@property (copy) NSMutableArray *array;
@property (nonatomic, copy) NSMutableArray *array; //overridden method is non-atomic as it is coded and should be reflected here.

If you wish to use (copy) on a mutable object you must override the "setter" method as follows...

- (void)setArray:(NSArray *)newArray {

    if ( array != newArray ) { 
        [array release];
        array = [newArray mutableCopy];
//      [array retain]; // unnecessary as noted by Georg Fritzsche
    }

    return;
}

NOTE: You will get a compiler warning: Incompatible Objective-C types initializing 'struct NSArray *', expected 'struct NSMutableArray *' I chose to declare the newArray parameter as an (NSArray *), because you are given the flexibility to have any array passed and correctly copied to your (NSMutableArray *) variable. If you wish to declare the newArray parameter as an (NSMutableArray *) you will still need to leave the mutableCopy method in place to get your desired results.

Cheers to Georg! Z@K!

Zak
Note that your property declaration doesn't have to match the ivar type, so you should use `@property (nonatomic, copy) NSArray *array;` in your case. Also note that your property is implicitly atomic, but the implementation of your setter is not - see [Atomicity](http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/ObjectiveC/Articles/ocProperties.html#//apple_ref/doc/uid/TP30001163-CH17-SW28).
Georg Fritzsche
Finally, `-mutableCopy` already returns a retained instance, thus there is no need for `[array retain]` - see [Object Ownership Policy](http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmObjectOwnership.html#//apple_ref/doc/uid/20000043-SW1).
Georg Fritzsche
Thank you again, Mr. Fritzsche. I was curious about that extra retain, but it seemed to work in my program. Apparently I have a bug elsewhere. :-/ You are the man, thank you for contributing on this post and, inadvertently, my program; I appreciate your time!Sincerely,Z@K!
Zak
While your @property declaration doesn't have to match, switching the property to NSArray instead of NSMutableArray has raised several compiler warnings everywhere I use a NSMutableArray method on self.array, saying, "'NSArray' may not respond to '-addObject:'". So I have opted to leave the NSMutableArray in place in the @property declaration and deal with the one compiler warning described in NOTE:.
Zak
My additional retain comes from this... `@property (copy) NSArray *anotherArray; @property (nonatomic, copy) NSMutableArray array;` when I set anotherArray equal to array `anotherArray = array; array = nil;` It was releasing all the objects inside of anotherArray, even though it has (copy) on the @property. This was blowing my mind. The additional retain I was putting in the setter was offsetting the release. I was able to fix it all by changing my code to... `anotherArray = [array copy];` Simple enough.
Zak
I ended up selecting my own answer, because it would give the best detail to someone else looking for this information, but I would like to give all credit to Georg Fritzsche; this is really his answer reformatted...
Zak