Objective-C Memory Management

What is Memory Management in Objective – C

  • Memory management is the process of allocating memory, using it and freeing it once it is no longer used
  • Objective of effecting memory management is to use as little memory as possible
  • In objective C, there are two methods for Application memory management
    • MRR – Manual Retain Release
    • ARC – Automatic Reference Counting
  • There are two main problems resulting in incorrect memory management
    • Over-freeing or Over-releasing, resulting in crashes
    • Not freeing data that is no longer used leading to leaks

 

Basic Rules of memory management

  • Memory management is based on Object-Ownership
  • An object can have more than one owner, till the time an owner exists for an object, the object continues to exist
  • If there are no owners for an object, the object is destroyed automatically by the run time.

Object Ownership

  • We own any object that we create using
    •  alloc
    • init
    • copy
    • mutableCopy
  • We take ownership of an object by using retain
  • If we do not need an object anymore then we should relinquish ownership using release or autorelease
  • We should never release an object that we do not own.

Retain Count

-(void)testRetainCount
{
 NSMutableArray *arrayOne = [[NSMutableArray alloc] init]; //arrayOne Retain Count = 1
 NSMutableArray *arrayTwo = [NSMutableArray new];  //arrayTwo retain count = 1
 NSMutableArray *arrayThree = [arrayTwo mutableCopy]; //arrayThree retain count = 1
 NSMutableArray *arrayFour = [arrayTwo mutableCopy];  //arrayFour retain count = 1
 NSMutableArray *arrayFive = [NSMutableArray arrayWithArray:[arrayTwo copy]]; //arrayFive retain count = 1
 [arrayOne retain]; //arrayOne retain count = 2
 [arrayOne retain]; //arrayOne retain count = 3
 [arrayOne release]; // arrayOne retain count = 2
 [arrayOne release]; // arrayOne retain count = 1
 [arrayOne release]; // arrayOne retain count = 0
 //Trying to access arrayOne after this point will lead to a crash
}

Developers should not rely on retainCount, the only thing developers should focus is on managing the object ownership using retain, release and autorelease. 

Release & Autorelease

  • Release – Once an object is created using (alloc, init) or new has to be released when the object is no longer needed.
  • Auto Release – autorelease is used to send the object no longer in use a deferred release message.
  • The object life time is comparatively more when autorelease is used rather than release

Release Sample

NSMutableArray *dataArray = [[NSMutableArray alloc] init];
 for (int i=0; i<10;i++)
 {
 NSString *str = [[NSString alloc] initWithFormat:@"Iteration %d",i];
 [dataArray addObject:str];
 [str release]; //Safe to release here 
 }
 NSLog(@"dataArray %@",dataArray);
 //Write more code for using dataArray
 [dataArray release]; //Safe to release the dataArray once it not required anymore

Autorelease sample

-(NSMutableString *)dataString
{
 NSMutableString *mutString = [[NSMutableString alloc] init];
 [mutString appendFormat:@"NSDate is %@ & Calendar Date is %@",[NSDate date],[NSCalendarDate calendarDate]];
 return [mutString autorelease];
//or
/*
NSMutableString *mutString = [[[NSMutableString alloc] init] autorelease];
[mutString appendFormat:@"NSDate is %@ & Calendar Date is %@",[NSDate date],[NSCalendarDate calendarDate]];
return mutString;
*/
}
Calling Function
NSString *dateString = [self dataString];
NSLog(@"dateString %@",dateString);
Output : dateString NSDate is 2014-03-11 23:58:14 +0000 & Calendar Date is 2014-03-11 16:58:14 -0700 

Convenience Constructors

 If convenience constructors are used, developers are not responsible for doing memory management for objects returned by the convenience constructors for: eg

NSArray *dataArrayOne = [NSArray arrayWithObjects:@"Object 1",@"Object 2",@"Object 3", nil];
NSArray *keyArray = [NSArray arrayWithObjects:@"key1",@"key2",@"key3", nil];
NSDictionary *dictionaryOne = [NSDictionary dictionaryWithObjects:dataArrayOne forKeys:keyArray];
There is no need to manage the memory for the local variables dataArrayOne, keyArray & dictionaryOne

Relinquish Ownership using Dealloc

Dealloc method is a method defined in NSObject and is invoked automatically when there are no owners for the object.

@interface KSEmployeeObject : NSObject
@property (retain) NSString *firstName;
@property (retain) NSString *lastName;
@property (retain) NSString *emailId;
@end

@implementation KSEmployeeObject
// ...
- (void)dealloc
 [_firstName release];
 [_lastName release];
 [_emailId release];

 //or
 //self.firstName = nil;
 //self.lastName = nil;
 //self.emailId = nil;
 [super dealloc]; //Must implement the superclass implementation of the dealloc method
}
@end

Use of Accessor Methods

@interface KSEmployeeObject : NSObject
@property (nonatomic,retain) NSString *firstName;
@end

@implementation KSEmployeeObject
@synthesize firstName;
- (void)dealloc
 [_firstName release];
 //or
 //self.firstName = nil;
 [super dealloc];
}
//If the firstName instance variable was not synthesized, 
//the getter and setter method can be implemented in the following way
/*
-(NSString *)firstName
{
 return _firstName;
 //or
 //return [[_firstName retain] autorelease]; //is more safe and increases the lifetime of the object.
}
-(void)setFirstName:(NSString *)newValue
{
 [newValue retain]
 [_firstName release];
 _firstName = newValue;
}
*/

//set property values using accessor methods
-(void)someMethod
{
 //Using a convenience constructor
 [self setFirstName:[NSString stringWithFormat:@"Debasis"]; 
 //or
 NSString *tempString = [[NSString alloc] initWithString:@"Debasis"];
 [self setFirstName:tempString];
 [tempString release];
}
@end

Note: The accessor methods should not be called from the init methods. developers are required to set the default values by directly accessing the property.

Important Notes:

1. Avoid retain cycles
Retaining an object creates a strong reference to the object and the object will not get released till the time there is a strong reference to the object.
If a cyclic relationship exists between two objects.
i.e Object A has a strong reference to Object B and Object B has a strong reference to Object A, neither of the object will ever get released.
to avoid this always have a strong reference from a parent to a child and a weak reference from child to parent kind of relationship.

2. Avoid causing deallocation of objects that you are using.
– Temporary retain of objects and release once done.

3. Dealloc method should not be used to manage scare resource

4. Collection owns the objects that they contain.

5. Retain Count
– When you create an object, it has a retain count of 1.
– When you send an object a retain message, its retain count is incremented by 1.
– When you send an object a release message, its retain count is decremented by 1.
– When you send an object a autorelease message, its retain count is decremented by 1 at the end of the current autorelease pool block.
– If an object’s retain count is reduced to zero, it is deallocated.

Autorelease Pools

  • An autorelease pool is an instance of NSAutoreleasePool that “contains” other objects that have received an autorelease message.
  • When the autorelease pool is deallocated it sends a release message to each of those objects. An object can be put into an autorelease pool several times, and receives a release message for each time it was put into the pool.
  • Thus, sending autorelease instead of release to an object extends the lifetime of that object at least until the pool itself is released (the object may survive longer if it is retained in the interim).
  • Cocoa always expects there to be an autorelease pool available.If a pool is not available, autoreleased objects do not get released and you leak memory.
  • If you send an autorelease message when a pool is not available, Cocoa logs a suitable error message.

Sending an autorelease or retain to an auto release pool?

  • We create an NSAutoreleasePool object with the usual alloc and init messages, and dispose of it with release (an exception is raised if we send autorelease or retain to an autorelease pool).
  • An autorelease pool should always be released in the same context (invocation of a method or function, or body of a loop) in which it was created.
  • Autorelease pools are arranged in a stack, although they are commonly referred to as being “nested.” When we create a new autorelease pool, it is added to the top of the stack. When pools are deallocated, they are removed from the stack. When an object is sent an autorelease message, it is added to the current topmost pool for the current thread.

When should we create and destroy auto release pools

  • Writing a program that is not based on the Application Kit when there is no built-in support for autorelease pools.
  • If we spawn a secondary thread, we must create your own autorelease pool as soon as the thread begins executing e.g.:If we write a loop that creates many temporary objects, you may create an autorelease pool inside the loop to dispose of those objects before the next iteration.
  • Each thread in a Cocoa application maintains its own stack of NSAutoreleasePool objects. When a thread terminates, it automatically releases all of the autorelease pools associated with itself.
  • Autorelease pools are automatically created and destroyed in the main thread of applications based on the Application Kit,
    • So your code normally does not have to deal with them there. If you are making Cocoa calls outside of the Application Kit’s main thread, however, you need to create your own autorelease pool.This is the case if you are a Foundation-only application or if you detach a thread. If your application or thread is long-lived and potentially generates a lot of autoreleased objects,you should periodically destroy and create autorelease pools(like the Application Kit does on the main thread); otherwise,autoreleased objects accumulate and your memory footprint grows.
    • If your detached thread does not make Cocoa calls,you do not need to create an autorelease pool.

ARC – Automatic Reference Counting

  • ARC is a compiler feature that provides automatic memory management for Objective C Objects, so that developers can focus primarily on building application functionality and not worry about retain and releases.
  • ARC follows the same principles of Manual Memory Management.
  • Manual memory management although straightforward involved lots of details and rules, Rules such as,
    • when to use autorelease,
    • when to retain,
    • when to release,
    • when to use convenience constructor (e.g NSString stringWithFormat) vs [[NSString alloc] initWithString].
    • With manual memory management, developers were expected to know how to use memory management tools such as Instruments, Static Analyzer, Object Alloc etc to identify leaks, zombies etc.
  • ARC evaluates the object lifetime requirement and accordingly inserts appropriate memory management calls at compile time. The compiler also creates the dealloc method on behalf of the developer.

Rules or ARC

  • ARC enforces rules, which if not adhered to will give compile-time error.
  • Cannot invoke dealloc explicitly
  • Cannot implement retain, release, retainCount or autorelease
  • Dealloc methods can be implemented for managing resources other than instance variables.
  • If Dealloc is used, [super dealloc] call is not required and using [super dealloc] will lead to compile error.
  • CFRetain, CFRelease can be used with Core Foundation Type objects
  • Memory zones cannot be used.
  • “new” cannot be used for beginning a property declaration.
NSAutoreleasePool cannot be used, if required @autoreleasepool block can be used.
e.g
@autoreleasepool{
//Huge amount of temporary objects are created here
}

Projects having both ARC and Non-ARC code

Projects can have both ARC and Non-ARC code, when a project is created to use ARC, the –fobj-arc compiler flag is set.

The ARC can be disabled for specific classes using the –fno-objc-arc.
Xcode→Project→Build Phase→Compile Sources→ Double Click on class and set the –fno-objc-arc

Property attributes in ARC

Strong, Weak, Unsafe_Unretained & autoreleasing

@property (strong) DdClass *ddObject;
is same as @property(retain) DsClass *ddObject;
Strong is default. Objects remain alive as long as there is a strong pointer to it.
@property(weak) DdClass *ddObject; is same as @property(assign) DdClass *ddObject;
except that in case of weak, the property value is set to nil instead of remaining as a dangling pointer as in the case of assign. “weak” specifies a reference that does not keep the referenced object alive. A weak reference is set to nil when there are no strong references to the object.

Unsafe_unretained does not keep the referenced object alive and at the same time does not set it to nil, So when the object being referenced is deallocated, the pointer is left dangling.

Autoreleasing – used to denote arguments that are passed by reference (id *) and are autoreleased on return.

With ARC the instance variables are implicitly initialized to nil.

Reference : https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmAutoreleasePools.html

Posted in Cocoa, Generic, iOS, Objective C Tagged with: , , , , , ,
0 comments on “Objective-C Memory Management
1 Pings/Trackbacks for "Objective-C Memory Management"

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Recent Posts


Hit Counter provided by technology news