Search This Blog

Wednesday, August 19, 2015

What are the difference between “copy” and “retain”?

  • Retaining and copying are two different things, the first is conceptually call-by-reference while the second is call-by-value 
  • Retain increases the retain count of an object by 1 and takes ownership of an object.
  • Whereas copy will copy the data present in the memory location and will assign it to the variable so in the case of copy you are first copying the data from a location assign it to the variable which increases the retain count.
  • Just remember that retain works on reference and copy works on value
  • simple difference: whenever you write retain it will increment the count and whenever you write copy it will make reference and does not increment the count for example let say:-
NSObject *ret=[obj1 retain]; It will increment the count by 1 in memory(count will become 2) NSObject *cop=[obj1 copy]; It will not increment the count here count will be 1 only it just create the new reference in memory. (count will remain same 1 only)

In a general setting, retaining an object will increase its retain count by one. This will help keep the object in memory and prevent it from being blown away. What this means is that if you only hold a retained version of it, you share that copy with whomever passed it to you.
Copying an object, however you do it, should create another object with duplicate values. Think of this as a clone. You do NOT share the clone with whomever passed it to you.
When dealing with NSStrings in particular, you may not be able to assume that whoever is giving you an NSString is truly giving you an NSString. Someone could be handing you a subclass (NSMutableString, in this case) which means that they could potentially modify the values under the covers. If your application depends on the value passed in, and someone changes it on you, you can run into trouble.

  1. assign
    • assign is a default property attribute
    • assign is a property attribute tells the compiler how to synthesize the property’s setter implementation
  2. copy:
    • copy is required when the object is mutable
    • copy returns an object which you must explicitly release (e.g., in dealloc) in non-garbage collected environments
    • you need to release the object when finished with it because you are retaining the copy
  3. retain:
    • specifies the new value should be sent “-retain” on assignment and the old value sent “-release”
    • if you write retain it will auto work like strong
    • Methods like “alloc” include an implicit “retain”

I'm inferring from the presence of retain, that you're employing MRR (manual retain and release). In MRR, this code results in:
@implementation ABC

-(void) fun1
{
  ObjectA * obj1 = [[ObjectA alloc] init];  // retainCount = +1
  ObjectA * obj2 = obj1;                    // unchanged

  // you have one instance of `ObjectA` with a retain count of +1
  // both `obj1` and `obj2` point to the same single instance
}

-(void) fun2
{
  ObjectA * obj1 = [[ObjectA alloc] init];  // retainCount = +1
  ObjectA * obj2 = [obj1 retain];           // retainCount = +2

  // you have one instance of `ObjectA` with a retain count of +2
  // both `obj1` and `obj2` point to the same single instance
}

-(void) fun3
{
  ObjectA * obj1 = [[ObjectA alloc] init];  // retainCount of `obj1` object = +1
  ObjectA * obj2 = [obj1 copy];             // retainCount of `obj2` object = +1

  // you have two instances of `ObjectA`, each with a retain count of +1
  // `obj1` points to one instance and `obj2` point to the other
}

-(void) fun4
{
  ObjectA * obj1 = [[ObjectA alloc] init];  // retainCount of `obj1` object = +1
  ObjectA * obj2 = [obj1 mutableCopy];      // retainCount of `obj2` object = +1

  // you have two instances of `ObjectA`, each with a retain count of +1
  // `obj1` points to one instance
  // `obj2` points to another instance, which is mutable copy of the `obj1` instance
}

@end

Clearly, in all of these cases, in MRR, if you don't do something with the ObjectA instances by the end of the method, you'll be leaking (because you're abandoning the last known reference to these objects). If using ARC, the necessary clean up is performed.
By the way, in the future, you should examine the results yourself. For example, if you added these diagnostic statements to the end of each method, you'd clearly see what's going on:
NSLog(@"%s: [obj1 (%p) retainCount] = %d", __FUNCTION__, obj1, [obj1 retainCount]);
NSLog(@"%s: [obj2 (%p) retainCount] = %d", __FUNCTION__, obj2, [obj2 retainCount]);
That displays the address of the object to which the variable points, as well as the object's current retainCount. Needless to say, you shouldn't be using retainCount in production code, but it's useful solely for diagnostic purposes.
By way, while it's good that you're trying to understand the memory management, I would suggest that you seriously consider using automatic reference counting (ARC). It makes memory management much easier (no retain, release or retainCount). And if you're determined to stick with manual retain and release (MRR), then make sure you run your code through the static analyzer ("Analyze" on Xcode's "Product" menu), as it's pretty decent in identifying the sort of problems that plague MRR code.

References:

No comments:

Post a Comment