🤯 50% Off! 700+ courses, assessments, and books

iPhone Application Development – Memory Management

Kanya Srinisavan
Share

Memory management is made so simpler and easier for developers with the advent of iOS5 Automated Reference Counting. We will understand the basic concepts of memory management and the significance of ARC in this tutorial.

Before iOS5, memory management was performed using Manual Reference counting.

  • If you create an object then you are the owner of the object and it is your responsibility to release the object ‘s memory when you no longer require that object.
  • You can also gain ownership to an object by sending ‘retain’ message to the object. Objects retained should also be released by its owners.
  • The object’s memory can be released by sending ‘release’ or ‘autorelease’ message to the object.
  • Auto release message can be sent if we want to reclaim the memory of the object in the future. When an object is autoreleased, it is added to the auto release pool. When the pool gets drained, the objects in the pool will be released.
  • The number of references or owners of an object is tracked using the reference count.
  • Whenever an object is initialized or retained, its reference count increases by 1 and when it is released it gets decremented by 1.
  • When the reference count of an object goes to zero, the object’s memory gets reclaimed. In other words, it gets de-allocated.

Sample 1:

Check out the reference count in the example below:

NSString *str1 = [[NSString alloc] init]; //Reference count is 1

[str1 retain]; // Reference count increments to 2

[str1 release]; Reference count decrements to 1
[str1 release]; Reference count  becomes 0 and memory gets reclaimed

Sample 2:

-(NSString *)sample{
    NSString *str = [[[NSString alloc] initWithString:@"Test"] autorelease];
    return str;
}

In Sample 2, if we call the release method on the str object, the object will be de-allocated before it is returned. This would not serve the purpose of the function. In such scenarios we will use autorelease or convenience methods.

The convenience methods take care of initializing and releasing the objects by themselves. In the sample below stringWithString is a convenience method.

-(NSString *)sample{
    NSString *str = [NSString stringWithString:@"Test"];
    return str;
}

When the object has no owners, its dealloc method is triggered. So we should implement the dealloc method in the custom classes that we create and clear all its holding resources.

In the sample below all the property variables are released in the dealloc method.

@interface Customclass : NSObject

@property (nonatomic,retain) NSString *str1;
@property (nonatomic,retain) NSString *str2;

@end

@implementation Customclass
@synthesize str1,str2;

-(void) dealloc
{
    [str1 release];
    [str2 release];
    [super dealloc];
}
@end

In manual memory management, there will be no issues if there is a perfect balance between retain and release calls.

Otherwise, the following threats may arise

  • Application may crash if you try to use an object that is released earlier or if the number of release messages exceeds the retain messages to the object.
  • Memory leaks may occur in your application if the release messages are lesser than the retain messages.

Thanks to iOS5, the nightmare of balancing the release with retains is completely solved with its Automatic Reference Counting.

The concepts are the same but the compiler does the tedious task of balancing the release with retains for the developers.

We should not use retain, release or autorelease methods. For the compiler to know when to release or retain an object we should use ownership qualifiers with the properties in the header files or reference variables.

Variable lifetime qualifiers

__strong pointer variables helps in retaining the object as long as the pointer points to it. Default qualifier.

NSArray * __strong arrSample;

__weak pointer variables specifies that the object’s memory can be reclaimed when there are no strong references to it. The object is set to nil when there are no strong references to it.

NSString * __weak strSample = [[NSString alloc] initWithString:@”hello”];
NSLog(@”%@”,strSample);

In the above example, the output would be nil. This is because the weak variable strSample has no other strong references to it.

__unsafe_unretained qualifier is similar to weak qualifier except that the object is not set to nil and is left as a dangling pointer. This qualifier is available only to support iOS4 wherein the weak qualifiers are not available. Do not use this qualifier, as it can result in crash scenarios because of its dangling pointers.

__autoreleasing is used for arguments that are passed as references.

Property lifetime qualifiers

All the above qualifiers except autoreleasing can be used for the properties in the header files. This would help the compiler to add appropriate release calls in the dealloc method.

Some other qualifiers are given below.

Nonatomic properties are not thread safe. Atomic is its opposite.

Assign property is used to assign a value to the variable and hence it is used for primitive data types.

Copy property is used when you feel that the object is mutable. This is used when you don’t want the value to be changed by other owners of the object.

Strong and weak time qualifiers help in avoiding strong reference or circular reference cycles. In a parent-child relationship, the parent should have strong reference to the child, and the child should have a weak reference to avoid the strong reference cycles.

ParentClass.h

#import "ChildClass.h"
@interface ParentClass : NSObject<SampleDelegate>

@property(nonatomic,strong) ChildClass *child;

@end

ParentClass.m

@implementation ParentClass
@synthesize child;
-(void) sampleMethod
{
    child = [[ChildClass alloc] init];
    child.delegate = self;

}

ChildClass.h

@protocol SampleDelegate<NSObject>

@end

@interface ChildClass : NSObject

@property(nonatomic,weak) id<SampleDelegate> delegate;
@end

ChildClass.m

@implementation ChildClass
@synthesize delegate;

@end

The delegate pattern is a good example of a retain cycle. The ParentClass has a strong reference to the ChildClass object. The delegate of the ChildClass is the ParentClass itself. This results in a circular reference. If the delegate of the ChildClass is strong, then neither the parent nor the child can be released. This would in turn cause memory leaks. So the delegate’s lifetime qualifier should be set as weak.

Even though ARC helps in retaining and releasing the objects for you, there could be circumstances when you should set the pointers to nil. If that isn’t done, your application could run out of memory. If you keep using strong references to the objects you create, the ARC cannot release them. So always try to understand the scope of the object and set them to nil if required.

Memory leaks can be detected and performance can be optimized with the help of the Instruments tools that come with the XCode. Explore the Instruments tools and do not forget to monitor the performance of your application before submitting it to the Appstore.

Conclusion

I hope you have acquired some understanding of how memory management is undertaken in iOS. Expertise in managing memory can be achieved only through experience. Let your exploration begin from here.

CSS Master, 3rd Edition