2

Pretty simple code, which I may say worked as intended in Xcode 4.1 but breaks in Xcode 4.2. Here is the offending code:

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

This is what the NSLogs display:

2011-11-03 15:42:59.967 TestProg[30413:c503] Target 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

When I execute the [target replaceOcurances... line of code I crash with-

Program received signal: "SIGABRT".

With the following in the console log -

2011-11-03 15:43:26.828 TestProg[30413:c503] * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attempt to mutate immutable object with replaceOccurrencesOfString:withString:options:range:'

My question is, WHERE am I attempting to mutate an immutable object ? Secondarily, why did this execute just fine in Xcode 4.1 ? Certainly all players looked mutable to Xcode 4.1. What's the difference in Xcode 4.2 ? I am missing something subtle here.

Ric
  • 796
  • 12
  • 28
  • `isKindOfClass` will return `YES` if the receiver is an instance of the given class or any class in its heirarchy, so `[@"this is immutable" isKindOfClass:[NSMutableString class]]` will return `YES`. – wulong Nov 03 '11 at 23:31

3 Answers3

3

I suspect that some of the strings are not really mutable strings (probably "target" since this is the one you are trying to modify). This really confused me for a while as well, but "isKindOf:" does not differentiate between mutable and non-mutable strings. I believe your NSLog queries will return YES even if the strings are not mutable.

If you stop in the debugger, you should be able to examine the objects and determine if they really are mutable or not (the "real" class names should show along with the object).

As far as why this worked in 4.1 and not 4.2, it is hard to say. It could be that some system routine that returns an NSString used to return an NSMutableString but no longer does.

If I am correct, you will need to go up the stack to find out where target came from. At some point, it might have been casted from an NSString to an NSMutableString.

Ron
  • 1,305
  • 2
  • 14
  • 22
  • Here is a link that backs up my statement about isKindOf: ... http://stackoverflow.com/questions/1788690/objective-c-how-to-check-if-variable-is-nsarray-or-nsmutablearray – Ron Nov 03 '11 at 23:32
  • Well, I believe you about the isKindOf. Same with Paul.s. But I have defined the variable that target reflects as an NSMutableString variable. It is as it is... I'm stumped as to how to massage or replace the code with such that accomplishes the functionality I am trying for. Any suggestions ? Thx. – Ric Nov 03 '11 at 23:52
  • Just some more things to check out ... you say you defined it as a NSMutableString, but where did it get created? Just because something is declared to be a NSMutableString doesn't mean that the pointer is really that object. The compiler is supposed to catch cases where pointers get mis-cast, but it is not perfect. Did you stop in the debugger and look at what the "target" object looks like? What class does the debugger say it is? Do you know the line of code that actually created the object? – Ron Nov 04 '11 at 03:47
0

Check the Apple docs for isKindOfClass:.

It basically says do not use this kind of check for class clusters and goes on to say

If you call a method that returns a class cluster, the exact type returned by the method is the best indicator of what you can do with that object.

It's entirely possible that they changed the type returned from the class cluster in this instance between SDK's.

Paul.s
  • 38,494
  • 5
  • 70
  • 88
0

The variable passed in "target" was defined and used everyplace as NSMutableString. ONE place in a method, called prior to the method under discussion, the variable was loaded from the contents of a file, and in that method I, from lack of experience, used the statement:

self.messageBody = fileContents;

fileContents was an NSString. Oops.

That apparently created a new memory location for messageBody with a different memory address, and was not Mutable. Question- does that make that in actuality a new object ? If so I guess it also created a memory leak as the first instance could no longer be released.

So when I executed the method under discussion, the object referenced by "target" was no longer Mutable and my replaceOccurancesOfString statement crashed.

This does not answer the anomaly of why it performed as desired in Xcode 4.1, but properly crashed in Xcode 4.2 . I came upon this due to the constructive prodding of Ron. Thx given.

Ric
  • 796
  • 12
  • 28
  • Glad you solved your problem! You also asked about a memory leak, and probably you were not leaking memory. It depends on how the property "messageBody" is defined, but if it is a "retain" property, then when you assign to self.messageBody, the old object is "released" and the new one is "retained". As far as why it crashed in XCode4.1 and not XCode4.2, it is probably because you changed SDKs when you upgraded. The old SDK probably returned an NSString that was really a NSMutableString, so your code worked by luck. In the new SDK, it returned a NSString which caused the crash. – Ron Nov 05 '11 at 04:39