为什么当所有变量都可变时尝试使用replaceOccurrencesOfString尝试变更不可变对象:

问题描述:

非常简单的代码,我可以说它在Xcode 4.1中按照预期工作,但在Xcode 4.2中中断。下面是有问题的代码:为什么当所有变量都可变时尝试使用replaceOccurrencesOfString尝试变更不可变对象:

-(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:YES

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:YES

当我执行[target replaceOcurances ...行代码我碰撞-

编程接收信号:“SIGABRT”。

随着在控制台日志以下 -

2011-11-03 15:43:26.828 TestProg [30413:C503] *终止应用程序由于未捕获的异常 'NSInvalidArgumentException',原因:“尝试mutate与replaceOccurrencesOfString不可变的对象:withString:options:range:'

我的问题是,我在哪里试图改变一个不可变的对象?其次,为什么在Xcode 4.1中执行得很好?当然,所有玩家都看到了Xcode 4.1的可变性。 Xcode 4.2 有什么区别?我在这里错过了一些微妙的东西。

+0

'isKindOfClass'将返回'YES',如果接收者是给定类或其任何类的实例,所以'[@“这是不可变的'isKindOfClass:[NSMutableString class]]'将返回'YES' 。 – wulong

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

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

至于为什么这工作在4.1而不是4.2,很难说。这可能是一些系统例程返回一个NSString用来返回一个NSMutableString,但不再。

如果我是正确的,你将需要去堆栈找出目标来自哪里。在某些时候,它可能已经从NSString转换为NSMutableString。

+0

这是一个链接,备份我关于isKindOf的声明:... http://stackoverflow.com/questions/1788690/objective-c-how-to-check-if-variable-is-nsarray-or-nsmutablearray – Ron

+0

那么,我相信你是关于isKindOf的。和保罗一样。但是我已经将目标反映的变量定义为NSMutableString变量。它就是这样......我很难理解如何按摩或替换代码,以实现我想要的功能。有什么建议么 ?谢谢。 – Ric

+0

只需要检查一下更多的东西......你说你将它定义为一个NSMutableString,但它是从哪里创建的?仅仅因为某些东西被声明为NSMutableString并不意味着指针就是那个对象。编译器应该能够捕获指针被误判的情况,但这并不完美。你停在调试器中,看看“目标”对象是什么样子?调试器说它是什么类?你知道实际创建该对象的代码行吗? – Ron

查阅Apple文档isKindOfClass:

它基本上是说不要使用这种支票类集群,并接着说

如果您调用返回一个类簇的方法,该方法返回的确切类型是最好的指标你可以用这个对象做什么。

这是完全可能的,他们改变了在这个实例中SDK类之间从类集群返回的类型。

在“target”中传递的变量被定义并用作NSMutableString的每个位置。一个地方的方法,之前所讨论的方法中调用,变量是从文件的内容加载,并在该方法我,从缺乏经验,使用的语句:

self.messageBody = fileContents; 

fileContents是一个NSString的。哎呀。

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

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

这并不回答为什么它在Xcode 4.1中执行所需的异常,但在Xcode 4.2中正确地崩溃。由于罗恩的建设性刺激,我找到了这个问题。 Thx给出。

+0

很高兴你解决了你的问题!你也问过内存泄漏,可能你没有泄漏内存。它取决于如何定义属性“messageBody”,但是如果它是“保留”属性,那么当您分配给self.messageBody时,旧对象将“释放”,并且新对象将“保留”。至于为什么它会在XCode4.1而不是XCode4.2中崩溃,这可能是因为您在升级时更改了SDK。旧的SDK可能会返回一个真正是NSMutableString的NSString,所以你的代码运气好。在新的SDK中,它返回了导致崩溃的NSString。 – Ron