Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cocoa: Testing to find if an NSString is immutable or mutable?

This produces an immutable string object:

NSString* myStringA = @"A";  //CORRECTED FROM: NSMutableString* myStringA = @"A";

This produces a mutable string object:

NSMutableString* myStringB = [NSMutableString stringWithString:@"B"];

But both objects are reported as the same kind of object, "NSCFString":

NSLog(@"myStringA is type: %@, myStringB is type: %@", 
[myStringA class], [myStringB class]);

So what is distinguishing these objects internally, and how do I test for that, so that I can easily determine if a mystery string variable is immutable or mutable before doing something evil to it?

like image 206
StringSection Avatar asked Jan 19 '10 10:01

StringSection


1 Answers

The docs include a fairly long explanation on why Apple doesn't want you to do this and why they explicitly do not support it in Receiving Mutable Objects. The summary is:

So don’t make a decision on object mutability based on what introspection tells you about an object. Treat objects as mutable or not based on what you are handed at the API boundaries (that is, based on the return type). If you need to unambiguously mark an object as mutable or immutable when you pass it to clients, pass that information as a flag along with the object.

I find their NSView example the easiest to understand, and it illustrates a basic Cocoa problem. You have an NSMutableArray called "elements" that you want to expose as an array, but don't want callers to mess with. You have several options:

  1. Expose your NSMutableArray as an NSArray.
  2. Always make a non-mutable copy when requested
  3. Store elements as an NSArray and create a new array every time it mutates.

I've done all of these at various points. #1 is by far the simplest and fastest solution. It's also dangerous, since the array might mutate behind the caller's back. But Apple indicates it's what they do in some cases (note the warning for -subviews in NSView). I can confirm that while #2 and #3 are much safer, they can create major performance problems, which is probably why Apple has chosen not to use them on oft-accessed members like -subviews.

The upshot of all of this is that if you use #1, then introspection will mislead you. You have an NSMutableArray cast as an NSArray, and introspection will indicate that it's mutable (introspection has no way to know otherwise). But you must not mutate it. Only the compile-time type check can tell you that, and so it's the only thing you can trust.

The fix for this would be some kind of fast copy-on-write immutable version of a mutable data structure. That way #2 could possibly be done with decent performance. I can imagine changes to the NSArray cluster that would allow this, but it doesn't exist in Cocoa today (and could impact NSArray performance in the normal case, making it a non-starter). Even if we had it, there's probably too much code out there that relies on the current behavior to ever allow mutability introspection to be trusted.

like image 182
Rob Napier Avatar answered Nov 12 '22 06:11

Rob Napier