【问题标题】:objective-c "mutating method sent to immutable object" errorObjective-c“发送到不可变对象的变异方法”错误
【发布时间】:2009-08-03 19:00:19
【问题描述】:

我对 Objective-c 很陌生,并尝试为 iphone 创建一个小应用程序。
除了这个小错误,我几乎完成了。实际上,我已经用谷歌搜索了几个小时以找到合适的解决方案,但不幸的是我无法找到有效的解决方案。
我在这里使用本教程来构建 UITableView:UITableView Tutorial 完整的错误信息如下所示:

* 由于未捕获的异常“NSInternalInconsistencyException”而终止应用程序,原因:“* -[NSCFArray insertObject:atIndex:]: mutating method sent to immutable object”

这是数据控制器标头: MyLinksDataController.h

@interface MyLinksDataController : NSObject {

NSMutableArray *tableList; //<---important part

}

- (unsigned)countOfList;
- (id)objectInListAtIndex:(unsigned)theIndex;
- (void)addData:(NSString *)data; //<---important part
- (void)removeDataAtIndex:(unsigned)theIndex;

@property (nonatomic, copy, readwrite) NSMutableArray *tableList; //<---important part

.....

和数据控制器方法: MyLinksDataController.m

#import "MyLinksDataController.h"

@implementation MyLinksDataController

@synthesize tableList;

- (id)init {

    if (self = [super init]) {

        NSLog(@"Initilizing DataController");
        //Instantiate list
        NSMutableArray *localList = [[NSMutableArray alloc] init];
        self.tableList = [localList copy];
        [localList release];

        //Add initial Data
        [self addData:@"AAAAAAAAAAAAAA"];
        [self addData:@"BBBBBBBBBBBBBB"];

    }

    return self;

}

-------------------稍后在源代码中---------- ----------------------

- (void)addData:(NSString*)data; {

    [tableList addObject:data]; //<---- here the app crashes

}

我非常感谢任何帮助。

提前感谢您的支持。

丹尼尔

【问题讨论】:

    标签: objective-c nsstring nsmutablearray immutability


    【解决方案1】:

    将 copy 消息发送到 NSMutableArray(如 init 中的以下语句所示)返回不可变副本。

    self.tableList = [localList copy];
    

    Cocoa 文档使用词不可变 来指代只读的、初始化后不能更改的对象。因此,对 addObject: 的后续调用失败并显示错误消息。

    请注意上面的赋值语句不会触发任何编译器警告。 copy 返回一个 id,它非常适合 - 就编译器而言 - 在 NSMutableArray* tableList 中。这里也没有运行时错误,因为没有消息传递;一个 NSArray 指针只是放在一个 NSMutableArray 指针变量中。

    要获取可变副本,请改用 mutableCopy。

    请注意,copy 和 mutableCopy 都会创建一个新数组并将原始内容复制到其中。副本中的更改不会反映在原件中。如果您只需要对原始数组的另一个引用,请改用 retain。

    您可以在copyWithZone reference 和NSMutableCopying protocol reference 的讨论部分找到更多详细信息。

    【讨论】:

      【解决方案2】:

      基本上,您遇到了 Cocoa 的内存管理规则 (specifically, these details)。如果存在具有immutable version 和可变版本的对象,则向对象发送-copy 将返回不可变对象。

      让我们逐步了解相关部分。

      NSMutableArray *localList = [[NSMutableArray alloc] init];
      

      这将创建一个您拥有的新的空可变数组。很好。

      self.tableList = [localList copy];
      

      这会创建一个空数组的不可变副本。此外,您拥有这个新创建的副本。这是您目前拥有的两个对象。

      这还将您复制的对象分配给tableList 属性。我们来看看属性声明:

      @property (nonatomic, copy, readwrite) NSMutableArray *tableList;
      

      这个属性是用copy attribute声明的,所以每当一个新的值被分配给它时,另一个-copy方法被发送给它。然而,这第三个副本并不归您所有——它归对象所有。

      [localList release];
      

      这会释放原始的空可变数组。很好,但是你在第二行中制作的那个仍然漂浮在周围,未发布。那是内存泄漏。

      如果您确实需要某个东西的可变副本,您需要-mutableCopy 方法。 (这些方法的文档可以在NSCopying 和NSMutableCopying 下找到。)但是,您永远不会将某个可变版本的内容放入具有copy 属性的属性中,因为它会将-copy 发送到无论分配什么。您的属性应使用retain 属性而不是copy 属性,初始化它的代码应如下所示:

      NSMutableArray *localList = [[NSMutableArray alloc] init];
      self.tableList = localList;
      [localList release];
      

      或者,更短的版本:

      self.tableList = [NSMutableArray array];
      

      在这种情况下无需复制任何内容,您只是在创建一个新对象。

      【讨论】:

      • 如果真的需要复制属性,那么替换保留属性将是一个糟糕的解决方案。由于没有 mutablecopy 关键字,因此将属性声明为副本,但要编写自己的访问器,而不是使用合成访问器。
      • 好点。我还要补充一点,我几乎从未见过宣传NSMutableArray 属性的对象。我的纯粹主义者会希望看到一个自定义 setter 存储对象的可变副本,以及一个自定义 getter 返回自动释放的不可变副本。
      【解决方案3】:

      如果你从另一个对象分配 localList 可能是不可变的,在这种情况下它可以通过这种错误。

      希望对你有帮助。

      self.tableList = [localList mutableCopy];
      

      【讨论】:

        【解决方案4】:

        嗨,而不是 mutableCopy,我相信“strong”也可以用来解决这个问题。我的代码中也有类似的问题,因为使用“copy”而不是“strong”。所以下面一行:

        @property (copy, nonatomic) NSMutableArray *computers;
        

        应该是:

        @property (strong, nonatomic) NSMutableArray *computers;
        

        希望对像我这样犯错的初学者有很大的帮助。

        【讨论】:

          【解决方案5】:

          这将解决问题:

          NSMutableArray *localList = [[NSMutableArray alloc] init];
          self.localList = [[NSMutableArray alloc]initWithArray:localList];
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-08-13
            • 2016-11-17
            • 1970-01-01
            相关资源
            最近更新 更多