Copy vs. Retain for NSString*

This is a dilemma/difficulty that every Objective-C developer (at least the ones who want to understand what their code does) runs into: Which @property attribute should be used for NSString*, (retain) or (copy)? There are so many good answers to this question out there, so I will not be adding a new one to the pool. According to me, the best answer/discussion is here (not surprisingly, it is from Stackoverflow).

If you look at the discussion above (or any other discussions on the web), the consensus is that copy should be used for NSString*. I, of course, agree with this answer but my problem is mostly with the reasoning.

By Googling this question, an average programmer will end up with the following statement in his/her head: For attributes whose type is an immutable value class that conforms to the NSCopying protocol, you almost always should specify copy in your @property declaration.

This answer is good, but I think it is only 50% of what you should learn from this topic; I think one can dig deeper.

Let's say you have the following class:
 @interface MyClass : NSObject {  
   NSString* myString;  
 }  
 @property (copy) NSString* myString;  
In this class, you have one variable of type NSString*. That means, you have a dependency on the NSString class. That also means you should know certain details about NSString; one such information is that it is immutable.

If that were all you had to know, then this whole discussion would become useless. The choice between (retain) & (copy) would not matter at all; as in both cases, you basically copy the state of the immutable object upon initialization and that state would never change. This is actually the ideal case (more on that later). However, there is a catch! The library also offers a NSMutableString class that subclasses the NSString class.

Rules have changed a little bit now; the users of your 'MyClass' may pass in an instance of 'NSMutableString*' for setting the 'myString' field. Basically, your assumption that you had a immutable field is no longer valid. Now, the choice of (retain) vs. (copy) will yield different results. The combination of passing in 'NSMutableString*' and having (retain) as the property attribute will cause problems for you because you were thinking that 'myString' field was immutable and its value wouldn't be modified once it is set.

To me, this is a clear violation of the Liskov Substitution Principle. Behaviour-wise, NSMutableString and NSString may be substitutable, however when it comes to memory-management, clearly there is a problem. Actually, to analyze this issue, we don't even have to dig deep and understand the details of Objective-C. The question can simply be formulated as: "Should immutable classes be subclassed with mutable versions?". With the (retain) vs. (copy) problem in hand, I think the answer turns out to be 'No: Such a design causes developers' assumptions to be invalidated with respect to memory management concerns'.

[The post focuses on NSString and NSMutableString classes; however they are just example classes. The discussion could be broadened to any mutable classes that subclass an immutable class.]

Choosing (retain) or (copy) for NSString* typed variables seems like easy task; googling for 5 minutes would give you the result. However, it is really important to understand why this choice creates so much confusion (it has been asked many times in different forums). Only when this is well-understood, one could be a lot more confident about the code that s/he is producing.

Comments

Popular Posts