4

I have a problem here. Or Maybe I'm really tired...

I have a class :

@interface THECLASS : UIViewController <UITableViewDelegate> {
    NSMutableArray* param;
}

@property(nonatomic, retain) NSMutableArray* param;

Inside that class I have a method that is called when the user clicks on a UISwitch inside a tableViewCell (I build the params into a IBAction method that is not shwon here) :

@synthesize param;

- (void) changedSelectorValue:(NSIndexPath*)indexPath isOn:(BOOL)isOn {
    [self.param addObject:@"eeeee"];
}

This crashes the app with the following log :

2011-02-04 01:31:02.548 Learning Project[3895:207] -[__NSArrayI addObject:]: unrecognized selector sent to instance 0x630c960
2011-02-04 01:31:02.549 Learning Project[3895:207] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayI addObject:]: unrecognized selector sent to instance 0x630c960'
*** Call stack at first throw:
(
    0   CoreFoundation                      0x00fc4be9 __exceptionPreprocess + 185
    1   libobjc.A.dylib                     0x011195c2 objc_exception_throw + 47
    2   CoreFoundation                      0x00fc66fb -[NSObject(NSObject) doesNotRecognizeSelector:] + 187
    3   CoreFoundation                      0x00f36366 ___forwarding___ + 966
    4   CoreFoundation                      0x00f35f22 _CF_forwarding_prep_0 + 50
    5   Learning Project                    0x00019463 -[ChoixJoursDisponibiliteController changedSelectorValue:isOn:] + 644
    6   Learning Project                    0x000190db -[ChoixJoursDisponibiliteController clickChangedSelectorValue:] + 307
    7   UIKit                               0x002f6a6e -[UIApplication sendAction:to:from:forEvent:] + 119
    8   UIKit                               0x003851b5 -[UIControl sendAction:to:forEvent:] + 67
    9   UIKit                               0x00387647 -[UIControl(Internal) _sendActionsForEvents:withEvent:] + 527
    10  UIKit                               0x004c9c6d -[UISwitch _onAnimationDidStop:finished:context:] + 201
    11  UIKit                               0x00327665 -[UIViewAnimationState sendDelegateAnimationDidStop:finished:] + 294
    12  UIKit                               0x003274f7 -[UIViewAnimationState animationDidStop:finished:] + 77
    13  QuartzCore                          0x01eab6cb _ZL23run_animation_callbacksdPv + 278
    14  QuartzCore                          0x01eab589 _ZN2CAL14timer_callbackEP16__CFRunLoopTimerPv + 157
    15  CoreFoundation                      0x00fa5fe3 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 19
    16  CoreFoundation                      0x00fa7594 __CFRunLoopDoTimer + 1220
    17  CoreFoundation                      0x00f03cc9 __CFRunLoopRun + 1817
    18  CoreFoundation                      0x00f03240 CFRunLoopRunSpecific + 208
    19  CoreFoundation                      0x00f03161 CFRunLoopRunInMode + 97
    20  GraphicsServices                    0x018f9268 GSEventRunModal + 217
    21  GraphicsServices                    0x018f932d GSEventRun + 115
    22  UIKit                               0x0030542e UIApplicationMain + 1160
    23  Learning Project                    0x00002580 main + 102
    24  Learning Project                    0x00002511 start + 53
    25  ???                                 0x00000001 0x0 + 1
)
terminate called after throwing an instance of 'NSException'

param is filled from a caller view by something like :

NSMutableArray* filledArray = [NSMutableArray arrayWithObjects:@"SOME TEXT", nil] 
nextController.param = filledArray;

NSLog(@"%@", self.param); just before the crash gives :

2011-02-04 02:01:17.205 Learning Project[4223:207] (
    "JOURDISPO_MARDI"
)

Why does this crash ? The array is there, filled and not nil... It seems well defined... addObject is a NSMutableArray method... I don't understand

I don't show it here but there is the same log with removeObject.

3
  • How are you initializing the param property? Commented Feb 3, 2011 at 23:43
  • @Dave DeLong : it's a filled array from the caller view. It is ok, no problem with it. It's filled and elsewhere, i use it to do some displays. Commented Feb 3, 2011 at 23:44
  • it is filled outside by [NSMutableArray arrayWithObjects:@"SOME TEXT", nil] Commented Feb 3, 2011 at 23:46

5 Answers 5

3

Answer is buried in the comments to the "accepted" answer. I came here from Google and it fixed my problem, so ... to make it clearer, based on @e.James's comment:

When you implement the "NSCopying" protocol, and implement "copyWithZone", you MUST NOT use "copy" on your internal mutable arrays - this DOES NOT copy the array (instead, it creates a non-mutable copy).

e.g. from my own code:

// Class: MyClass

@property(nonatomic, retain) NSMutableArray* mutArray;

-(id)copyWithZone:(NSZone *)zone
{
    MyClass* other = [[MyClass alloc] init];

//  other.mutArray = [self.mutArray copy]; // WRONG!
    other.mutArray = [self.mutArray mutableCopy]; // CORRECT!

    return other;
}
Sign up to request clarification or add additional context in comments.

Comments

2

For some reason you're calling the addObject: method on an instance of NSArray, as the stack trace indicates. Perhaps the synthesized getter of your param object returns an NSArray? Don't use the getter in your method:

- (void) changedSelectorValue:(NSIndexPath*)indexPath isOn:(BOOL)isOn {
    [param addObject:@"eeeee"];
}

Ultimately, the error is correct. You're calling a mutating method on an immutable object. Strange enough, in my testing this doesn't cause a compiler error or warning:

NSMutableArray *array = [[NSArray alloc] init];

Edit: Whether you want to believe it or not, somehow the param ivar is being set to an instance of an NSArray. Override the setter:

- (void)setParam:(NSMutableArray *)p {
    if (param != p) {
        [param release];
        param = [p retain];
    }
}

Then add a breakpoint on that method, and check to see when an NSArray is being passed in.

Comments

1

Your property is defined as retain, and you say that param is "a filled array from the caller view". This could be your problem.

For example, if you do the following:

NSArray * filledArray = [NSArray arrayWithObjects:..., nil];
theClassInstance.param = filledArray;

You will have retained a non-mutable array.

Edit: To debug the setting of param, you could do the following in your .m:

@dynamic param;
- (NSMutableArray*)param { return param; }
- (void)setParam:(NSMutableArray*)newArray {
    NSLog(@"setting new param from class: %@",
          NSStringFromClass([newArray class]));
    [newArray retain];
    [param release];
    param = newArray;
}

13 Comments

@e.James : No, it's a NSMutableArray outside and not a NSArray.
I think it would have to be [filledMutableArray mutableCopy], otherwise you just get NSArray again. Not sure if this is even the problem, though. Can you add NSLog(@"%@", param); in your method just before the crash to see what kind of object it is?
Actually, scratch the NSLog. It is obvious that param is somehow being set to a non-mutable NSArray somewhere in your code. It might be beneficial to create a custom setter for the param property and then log its behaviour before the crash happens.
I added example debugging code in my answer. Using that, you should be able to determine when param becomes immutable
The custom setter wouldn't solve your problem. I suggest it as a way to debug. The log output should tell you where you are getting a non-mutable array from.
|
0

What does your init method look like? It should look something like this...

- (id)init
{
    self = [super init];

    self.param = [NSMutableArray array];

    return self;
}

3 Comments

read the comments, it's filled by the caller view. No problem with this array, it's filled and used with no problem elsewhere in the Controller. Even the debugger show a correct array.
For the sake of doing things properly, you should always ensure that [super init] does not return nil before performing further initialization. I use: if ((self = [super init]) == nil) { return nil; } in the first line
As above, be sure to check for [super init] returning nil. Also, on the iPhone (though in any case, really), it's a better idea to avoid autoreleased objects for ivars, it's just a waste of computation time.
0

After struggling a lot i found the solution for my problem. In my case the problem was,

I have a mutable dictionary and array

@property(strong,nonatomic)NSMutableDictionary *dictInfo;
@property(retain,nonatomic) NSMutableArray *userAccountsList;

And I am adding server array list to userAccountsList array like this

 self.dictInfo = [jsonObject valueForKey:@"customerAccountList"];

I have taken another array and adding server array list to that array

array = [self.dictInfo valueForKey:@"banker"];

and finally adding array to userAccountsListArray like this

                userAccountsList = [NSMutableArray arrayWithArray:array];

And here adding additional object to array

                [userAccountsList addObject:@"Other Account"];//I was getting error here.

                [tableViewObj reloadData];

Hope it helps someone. Please vote up if it helps.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.