views:

175

answers:

2

Having recently done some development for iPhone, I've come to notice an interesting design pattern used a lot in the iPhone SDK, regarding object mutability.

It seems the typical approach there is to define an immutable class NSFoo, and then derive from it a mutable descendant NSMutableFoo. Generally, the NSFoo class defines data members, getters and read-only operations, and the derived NSMutableFoo adds on setters and mutating operations.

Being more familiar with C++, I couldn't help but notice that this seems to be a complete opposite to what I'd do when writing the same code in C++. While you certainly could take that approach, it seems to me that a more concise approach is to create a single Foo class, mark getters and read-only operations as const functions, and also implement the mutable operations and setters in the same class. You would then end up with a mutable class, but the types Foo const*, Foo const& etc all are effectively the immutable equivalent.

I guess my question is, does my take on the situation make sense? I understand why Objective-C does things differently, but are there any advantages to the two-class approach in C++ that I've missed? Or am I missing the point entirely?

Not an overly serious question - more for my own curiosity than anything else.

A: 

Thoughts...

Mac, as you know, in C++, you can think of "A" and "const A" as two different types, with their only relations being

  1. "A" and "A &" can be implicitly cast to "const A" and "const A &", etc...
  2. "const A &" can be const_cast to "A &", etc...

The compiler handles the inheritance and propagation of the type-modifier through expressions, etc.

I would guess that the NS folks chose the ..Mutable.. convention for a couple reasons. My first guess is that I believe that when NextStep was being originally pioneered, C did not have support for "const" and it still doesn't have Object-Orientation. And more importantly, they wanted certain optimizations in their mutable vs. immutable object implementations. For example, in an immutable string class like NSString, it can be useful to "pool" strings. This allows for duplicate strings to be atomized and for the process to potentially use less memory. (It has certain downsides, but there are always tradeoffs.)

In C++ you could head in the same direction by first understanding copy-on-write. std::string is said to do this.

Interesting topic,
Will Bradley

Will Bradley
`std::string` is allowed to use COW to save on copying, but is not required to do so by the standard. Since COW is actually a performance hit in a multithreaded environment, few implementations still do this.
Billy ONeal
+1  A: 

Objective-C is too dynamic. In C++ const-qualification is enforced at compile-time, and any violations of const-qualification at runtime (such as modifying a const-qualified object through a non-const-qualified pointer) is undefined behaviour.

It is partly the same as the reason why there are no private methods in Objective-C. You are free to send whatever message you want to any object. The runtime dispatch takes an object and a message, and resolves a method implementation to invoke.

if const qualified objects could only invoke const qualified methods, it would completely ruin the dynamic nature of Objective-C and Foundation because such a check would need to be done at runtime (first check would determine whether the message being sent resolves to a const-qualified implementation for that specific instance, and another check to determine whether the instance itself was const-qualified). Consider this theoretical example:

NSArray *mutableArray = [[NSArray alloc] init];

NSString *mutableString = @"I am a mutable string";
const NSString *immutableString = @"I am immutable because I am const-qual'd";

[mutableArray addObject:mutableString];
[mutableArray addObject:immutableString]; // what happens?!

// and what happens here (both immutable and mutable strings would respond
// to the same selectors because they are the same class):
[mutableArray makeObjectsPerformSelector:@selector(aMutableOperation)];

Suddenly you lose dynamics. As it is now, mutable and immutable objects can sit together in an immutable or mutable collection.

Having a mutable subclass keeps the dynamic nature of Objective-C and keeps the runtime simple. There was a similar topic a while ago about private methods.

dreamlax