iPhone - Crash using addObject on a NSMutableArray

2019-01-19 06:54发布

问题:

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.

回答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;
}


回答2:

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;
}


回答3:

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.



回答4:

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

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

    self.param = [NSMutableArray array];

    return self;
}


回答5:

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.