69

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?

Community
  • 1
  • 1
Zak
  • 12,213
  • 21
  • 59
  • 105

9 Answers9

85

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.

Roger
  • 911
  • 2
  • 9
  • 15
Georg Fritzsche
  • 97,545
  • 26
  • 194
  • 236
  • 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 Jul 10 '10 at 17:29
  • 1
    The link to the Objective-C guide is broken. – johnnieb Jul 09 '13 at 22:53
76

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.

Zak
  • 12,213
  • 21
  • 59
  • 105
11

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
  • 12,213
  • 21
  • 59
  • 105
  • 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 Jul 10 '10 at 23:28
  • 1
    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 Jul 10 '10 at 23:29
  • 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 Jul 11 '10 at 01:01
  • 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 Jul 11 '10 at 01:57
  • 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 Jul 11 '10 at 03:01
  • 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 Jul 13 '10 at 15:51
  • If you implement you own setter as per above. Do you then remove the @synthesize statement? –  Nov 04 '10 at 16:55
  • You need to use the @synthesize statement, so it will implement the "getter" method for you. Then you will override the "setter" method it creates as you build your own like the above example. – Zak Nov 05 '10 at 13:43
4

Have some indexes (into a data array elsewhere) and wanted to have them in numerical order (for a good reason). Crashing until added mutableCopy at the end. Totally puzzled, until I recalled that using Objective-C literal @[] returns a non-mutable array.

NSMutableArray *a = [@[@(self.indexA), @(self.indexB)] mutableCopy];
NSLog(@"%@", a);
[indexArray sortUsingComparator: ^(NSNumber *obj1, NSNumber *obj2) {
    return [obj1 compare:obj2];
}];
NSLog(@"%@", a);

Thanx, Zak!

JOM
  • 8,139
  • 6
  • 78
  • 111
3

I was getting the same error, even though my properties were strong (using ARC) and I allocated the array with NSMutableArray.

What was happening was that I was archiving the mutable array (as it contains custom objects) for future use and when decoding it, it returns an immutable copy.

Hope it helps anyone out there.

olivaresF
  • 1,369
  • 2
  • 14
  • 28
1

The error came about as a result of attempting to add an object to an NSMutableArray type that was actually pointing to an NSArray object. This type of scenario is shown in some demo code below:

NSString *test = @"test";
NSMutableArray *mutableArray = [[NSMutableArray alloc] init];
[mutableArray addObject:test];
NSArray *immutableArray = [[NSArray alloc] init];
mutableArray = immutableArray;
[mutableArray addObject:test]; // Exception: unrecognized selector

From the above code, it is easy to see that a subclass type is being assigned to a superclass type. In Java, for instance, this would immediately have been flagged as an error (conversion error between types), and the problem resolved fairly quickly. To be fair to Objective-C, a warning is given when attempting to perform an incompatible assignment, however, this simply just does not seem to be enough sometimes and the result can be a real pain for developers. Fortunately, this time around, it was not myself who bore most of this pain :P

Baljeet Singh
  • 453
  • 5
  • 15
0

I got bitten by this exception for a typo I've made, maybe it'll save someone 5 min. of their time:

I wrote:

NSMutableArray *names = [NSArray array];

instead of:

NSMutableArray *names = [NSMutableArray array];

The compiler has no problem with that because NSMutableArray is also an NSArray, but it crashes when trying to add an object.

marmor
  • 27,641
  • 11
  • 107
  • 150
0

I had similar error saying : unrecognized selector sent to instance for NSMutable array.. After going through a lot of things, I figured out that I was using my mutable array as a property as

@property (assign, nonatomic) NSMutableArray *myMutableArray;

while copy pasting and not paying attention and it was causing the problem.

The solution us that I changed it to strong type(you can change it to any other type like strong/weak,etc.. depending your requirement). So solution in my case was :

@property (strong, nonatomic) NSMutableArray *myMutableArray;

So, be careful while copy pasting! Do check once.

Priyanka
  • 442
  • 1
  • 5
  • 13
-3

This exception can happen if one of the objects in the array is null.

ho322
  • 15