iPhone - 在NSMutableArray上使用addObject时崩溃

4
我在这里有一个问题。或许是我真的很累了...
我有一节课:
@interface THECLASS : UIViewController <UITableViewDelegate> {
    NSMutableArray* param;
}

@property(nonatomic, retain) NSMutableArray* param;

在那个类中,我有一个方法,当用户点击tableViewCell内的UISwitch时调用(我将参数构建到一个未显示在此处的IBAction方法中)。
@synthesize param;

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

这会导致应用程序崩溃,并显示以下日志:
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是从调用者视图中填充的,类似于以下内容:
NSMutableArray* filledArray = [NSMutableArray arrayWithObjects:@"SOME TEXT", nil] 
nextController.param = filledArray;

在崩溃之前执行

NSLog(@"%@", self.param);会输出:

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

为什么会崩溃? 数组已经存在,填充了数据且不为空...它似乎被正确定义了...addObject是NSMutableArray的方法...我不明白。
我这里没有展示,但是使用removeObject也会出现相同的日志。

你是如何初始化 param 属性的? - Dave DeLong
@Dave DeLong:从调用者的角度来看,这是一个填充的数组。它没问题,很好。我会在其他地方使用它来进行一些显示。 - Oliver
它是通过[NSMutableArray arrayWithObjects:@"SOME TEXT", nil]填充的。 - Oliver
5个回答

3
答案藏在“已接受”答案的评论中。我是从谷歌来到这里,它解决了我的问题,所以...根据@e.James的评论,为了使它更清晰:
当你实现“NSCopying”协议并实现“copyWithZone”时,你不能在内部可变数组上使用“copy” - 这不会复制数组(而是创建一个非可变副本)。
例如,来自我的代码:
// 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;
}

2

根据堆栈跟踪,你以某种方式在 NSArray 的实例上调用了 addObject: 方法。也许你的 param 对象的合成 getter 返回了一个 NSArray?在你的方法中不要使用 getter:

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

最终,错误是正确的。您正在对一个不可变对象调用可变方法。奇怪的是,在我的测试中,这并不会导致编译器错误或警告:

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

编辑:无论你是否愿意相信,param实例变量都被设置为NSArray的一个实例。重写setter方法:

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

然后在该方法上添加一个断点,并检查何时传递了NSArray。

1

您的属性被定义为retain,并且您说param是“来自调用者视图的填充数组”。这可能是您的问题所在。

例如,如果您执行以下操作:

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

你将拥有一个不可变的数组。
编辑:为了调试 param 的设置,你可以在 .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;
}

3
我认为应该使用 [filledMutableArray mutableCopy],否则你只会得到 NSArray。不过我不确定这是否是问题的根本原因。在崩溃发生之前的方法中加入 NSLog(@"%@", param); 以查看它是什么类型的对象。 - e.James
1
实际上,删掉 NSLog。很明显,在你的代码中,param 被设置为一个不可变的 NSArray 某个地方。创建一个自定义的 param 属性 setter,然后在崩溃发生之前记录其行为可能会有益处。 - e.James
1
我在我的答案中添加了示例调试代码。使用它,你应该能够确定param何时变为不可变的。 - e.James
1
自定义setter不会解决你的问题。我建议使用它作为调试的一种方式。日志输出应该会告诉你从哪里得到了一个不可变的数组。 - e.James
这里不需要使用@dynamic指令;只有在编译类时编译器无法看到访问器方法定义时才需要它。 - jscs
显示剩余8条评论

0
你的初始化方法是什么样子的?应该长成这个样子...
- (id)init
{
    self = [super init];

    self.param = [NSMutableArray array];

    return self;
}

请阅读注释,它由调用者视图填充。这个数组没有问题,在控制器的其他地方也被使用而没有问题。即使调试器显示正确的数组。 - Oliver
1
为了做好事情,您应该始终确保 [super init] 在执行进一步的初始化之前不返回 nil。我使用:if ((self = [super init]) == nil) { return nil; }在第一行。 - e.James
同上,一定要检查[super init]是否返回nil。此外,在iPhone上(实际上在任何情况下),最好避免使用自动释放对象作为ivars,这只是浪费计算时间的做法。 - Craig Otis

0

经过了很多的挣扎,我终于找到了解决我的问题的方法。在我的情况下,问题是:

我有一个可变字典和数组。

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

我正在将服务器数组列表添加到用户帐户列表数组中,就像这样

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

我已经创建了另一个数组,并将服务器数组列表添加到该数组中

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

最后,将数组添加到userAccountsListArray中,就像这样:
                userAccountsList = [NSMutableArray arrayWithArray:array];

在这里将附加对象添加到数组中

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

                [tableViewObj reloadData];

希望能对某些人有所帮助。如果有效,请投票支持。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接