Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best way to define an immutable class in Objective C

I am a newbie in Objective C and I was wondering what is the best way to define an immutable class in Objective-C (like NSString for example).

I want to know what are the basic rules one has to follow to make a class immutable.

I think that :

  • setters shouldn't be provided
  • if properties are used, they should be readonly
  • to "disable" Key Value Coding , accessInstanceVariablesDirectly must be override and return NO

Did I forget something ?

Thanks

like image 873
Patrick Marty Avatar asked Apr 05 '10 01:04

Patrick Marty


People also ask

How do you define a class in Objective C?

In Objective-C, a class is itself an object with an opaque type called Class . Classes can't have properties defined using the declaration syntax shown earlier for instances, but they can receive messages.

Can you create an immutable object that contains a mutable object?

If you want to encapsulate a mutable object into an immutable one, then you need to: Never return the mutable object. If you must to, then return a copy of the object. Avoid methods which can change the mutable object.


2 Answers

The first and foremost thing you should do is to include usage comments in your .h file that explain that this is an immutable class, along with the class's purpose and general usage guidance. Far too often people go to great lengths to try to "enforce" with the compiler what could be achieved by just informing the caller.

You should certainly not provide public setters or readwrite properties if you intend the class to be immutable (but of course you should provide private setters so that you can use accessors within the class; you should always avoid, even internally, messing with ivars directly except in a few places). I guess you could add your accessInstanceVariablesDirectly override if you saw this as a likely error on the part of the caller.

But the key to understanding Objective-C is to understand and embrace the fact that the caller is not the enemy. The called code does not need to be "protected" from the caller. The caller needs to be protected from likely error. Everyone is on the same side here; caller and called want the program to work.

The caller is the customer and should be treated as such. The customer is not always right, but the customer is always the customer. Sometimes that means protecting the customer from himself if there is an easy mistake he might make. NSAssert() is particularly useful for that. And providing public setters to an immutable class is almost tricking the caller into making a mistake, so that would be bad for everyone.

In any case, you shouldn't make your class overly complex to try to enforce immutability. The caller can almost (*) always violate encapsulation by accessing the struct directly (object->ivar). The caller would be foolish to do so, but you would be even more foolish to try to prevent it. Note the immutability, hide your setters and mark your properties readonly, and in almost all cases you should be fine.

(*) Yes, it's possible to even more hide your data by nesting a private struct/object as an ivar, but then the caller can still modify the data with pointer arithmetic so it's still not "enforced." Always ask yourself what problem you're really trying to solve.

like image 82
Rob Napier Avatar answered Oct 19 '22 15:10

Rob Napier


I believe they way I'd accomplish this is to have the header file contain only the publicly needed information. The rest would go in to the source file to limit possible override exposure.

Since Objective-C apparently has no definitive way of defining a class as final (sealed, etc), everything you'd be able to do isn't really all encompassing.

I've long ago came to the conclusion that you really can't use Objective-C like you'd use Java, C++ or C#. Objective-C is simply too different. In fact I believe there are drastic paradigm differences such as static vs. dynamic method dispatch/calls.

The reason I mention this is because perhaps no class in Objective-C is truly final. Perhaps this is by language design and not something you should try to get around. If you do, you'd ultimately needlessly complicate your code.

like image 22
Frank V Avatar answered Oct 19 '22 14:10

Frank V