【问题标题】:Why do I get "Attempt to mutate immutable object with replaceOccurrencesOfString:" when all vars ar Mutable当所有变量都是可变的时,为什么我会得到“尝试用 replaceOccurrencesOfString 改变不可变对象:”
【发布时间】:2011-11-03 23:08:39
【问题描述】:

相当简单的代码,我可以说它在 Xcode 4.1 中按预期工作,但在 Xcode 4.2 中中断。这是有问题的代码:

-(void)mergeDevData2Email:(NSMutableString *)target codeArray:(NSArray *)array1 valueArray:(NSArray *)array2 {
NSUInteger n = 0;

for (NSMutableString *aCode in array1) {
    if ([array2 count] > n) {
        NSMutableString *arg = [array2 objectAtIndex:(NSUInteger)n];

        NSLog(@"Target isKindOf NSMutableString: %@", ([target isKindOfClass:[NSMutableString class]]) ? @"YES" :@"NO");
        NSLog(@"aCode isKindOf NSMutableString: %@", ([aCode isKindOfClass:[NSMutableString class]]) ? @"YES" :@"NO");
        NSLog(@"arg isKindOf NSMutableString: %@", ([arg isKindOfClass:[NSMutableString class]]) ? @"YES" :@"NO");

        [target replaceOccurrencesOfString:aCode withString:arg options:NSLiteralSearch range:NSMakeRange(0, [target length])];
        n++;
    }
    else {
        break;
    }
}
}

这是 NSLogs 显示的内容:

2011-11-03 15:42:59.967 TestProg[30413:c503] 目标 isKindOf NSMutableString: 是

2011-11-03 15:42:59.968 TestProg[30413:c503] aCode isKindOf NSMutableString: YES

2011-11-03 15:42:59.969 TestProg[30413:c503] arg isKindOf NSMutableString: 是

当我执行 [target replaceOcurances... 代码行时,我崩溃了-

节目接收信号:“SIGABRT”。

在控制台日志中有以下内容-

2011-11-03 15:43:26.828 TestProg[30413:c503] * 由于未捕获的异常“NSInvalidArgumentException”而终止应用程序,原因:“尝试使用 replaceOccurrencesOfString:withString: 改变不可变对象:选项:范围:'

我的问题是,我在哪里尝试改变不可变对象?其次,为什么这在 Xcode 4.1 中执行得很好?当然,所有玩家在 Xcode 4.1 中看起来都是可变的。 Xcode 4.2 有什么不同 ?我在这里遗漏了一些微妙的东西。

【问题讨论】:

  • isKindOfClass 将返回 YES 如果接收者是给定类的实例或其层次结构中的任何类,因此 [@"this is immutable" isKindOfClass:[NSMutableString class]] 将返回 YES

标签: objective-c ios xcode4 xcode4.2 nsmutablestring


【解决方案1】:

我怀疑某些字符串并不是真正可变的字符串(可能是“目标”,因为这是您要修改的字符串)。这也让我困惑了一段时间,但是“isKindOf:”并没有区分可变字符串和非可变字符串。我相信即使字符串不可变,您的 NSLog 查询也会返回 YES。

如果您在调试器中停下来,您应该能够检查对象并确定它们是否真的是可变的(“真正的”类名应该与对象一起显示)。

至于为什么这在 4.1 而不是 4.2 中有效,很难说。可能是某些返回 NSString 的系统例程曾经返回 NSMutableString 但现在不再返回。

如果我是正确的,您将需要向上堆栈以找出目标的来源。在某些时候,它可能已经从 NSString 转换为 NSMutableString。

【讨论】:

  • 这里有一个链接支持我关于 isKindOf 的声明:...stackoverflow.com/questions/1788690/…
  • 好吧,关于isKindOf,我相信你。与 Paul.s 相同。但是我已经将 target 反映的变量定义为 NSMutableString 变量。它就是这样......我很难过如何按摩或替换代码以完成我正在尝试的功能。有什么建议么 ?谢谢。
  • 还有一些东西要检查......你说你将它定义为 NSMutableString,但它是在哪里创建的?仅仅因为某些东西被声明为 NSMutableString 并不意味着指针真的是那个对象。编译器应该捕获指针被错误转换的情况,但它并不完美。您是否在调试器中停下来查看“目标”对象的外观?调试器说它是什么类?你知道实际创建对象的代码行吗?
【解决方案2】:

查看 Apple 文档以获取 isKindOfClass:

它基本上说不要对类集群使用这种检查并继续说

如果您调用返回类簇的方法,则该方法返回的确切类型是您可以使用该对象做什么的最佳指标。

他们完全有可能在 SDK 之间更改了从该实例中类集群返回的类型。

【讨论】:

    【解决方案3】:

    在“target”中传递的变量被定义并作为 NSMutableString 到处使用。在讨论的方法之前调用的方法中的一个位置,变量是从文件的内容中加载的,在那个方法中,由于缺乏经验,我使用了以下语句:

    self.messageBody = fileContents;
    

    fileContents 是一个 NSString。哎呀。

    这显然为 messageBody 创建了一个具有不同内存地址的新内存位置,并且不是可变的。问题-这是否使它实际上成为一个新对象?如果是这样,我猜它也会造成内存泄漏,因为第一个实例无法再被释放。

    所以当我执行讨论的方法时,“target”引用的对象不再是可变的,我的 replaceOccurancesOfString 语句崩溃了。

    这并没有回答为什么它在 Xcode 4.1 中按预期执行,但在 Xcode 4.2 中正确崩溃的异常情况。由于罗恩的建设性推动,我想到了这一点。谢谢。

    【讨论】:

    • 很高兴您解决了问题!您还询问了内存泄漏,可能您没有泄漏内存。这取决于属性“messageBody”是如何定义的,但如果它是一个“保留”属性,那么当您分配给 self.messageBody 时,旧对象是“释放”,新对象是“保留”。至于为什么它在 XCode4.1 而不是 XCode4.2 中崩溃,可能是因为您在升级时更改了 SDK。旧的 SDK 可能返回一个 NSString,它实际上是一个 NSMutableString,所以你的代码很幸运。在新的 SDK 中,它返回了一个导致崩溃的 NSString。
    猜你喜欢
    • 2013-07-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-02
    • 2015-01-07
    • 1970-01-01
    相关资源
    最近更新 更多