Mac OS X now has support for garbage collection. But that facility is not available in iOS yet. Only reference count based memory management is available. So, it pays to have a good feel for how memory is allocated and deallocated.
We will use a very simple program to learn the basics. The interface will remain unchanged throughout this article.
@interface Address : NSObject { NSString* street; NSString* city; NSString* zip; } - (Address*) initStreet: (NSString*) s city: (NSString*) c zip: (NSString*) z; - (void) print; @end
Our first implementation will be very basic.
@implementation Address - (Address*) initStreet: (NSString*) s city: (NSString*) c zip: (NSString*) z { street = s; city = c; zip = z; return self; } - (void) print { NSLog(street); NSLog(city); NSLog(zip); } @end
Here, the the Address class does not take ownership of the various NSString objects sent to the initStreet method.
Let’s see a use of the class:
int main (int argc, const char * argv[]) { NSString *s = initWithCString: "101 Flora"]; NSString *c = initWithCString: "Miami"]; NSString *z = initWithCString: "33139"]; Address *a = initStreet:s city:c zip:z]; ; return 0; }
The code will work just fine. Except that there is mass scale memory leak. All four objects created in the main method will leak.
We can easily fix that by releasing the objects. A golden rule of memory management is that the owner of an object should free it. An entity that creates an object is the default owner of it.
int main (int argc, const char * argv[]) { NSString *s = initWithCString: "101 Flora"]; NSString *c = initWithCString: "Miami"]; NSString *z = initWithCString: "33139"]; Address *a = initStreet:s city:c zip:z]; ; /* Free all objects */ ; ; ; ; return 0; }
This is much better. Now we have stopped the memory leak.
The Address class still does not take ownership of the NSString objects. This can cause problem if an Address object needs to use the NSString objects after they are released. For example, the following is an error and will cause the program to crash.
int main (int argc, const char * argv[]) { NSString *s = initWithCString: "101 Flora"]; NSString *c = initWithCString: "Miami"]; NSString *z = initWithCString: "33139"]; Address *a = initStreet:s city:c zip:z]; ; ; ; ; /* Error. Will cause crash. */ ; ; return 0; }
Here we have a situation where an object must take ownership of other objects even though it did not create them in the first place. We will change the initStreet method to retain the NSString objects and there by taking ownership.
- (Address*) initStreet: (NSString*) s city: (NSString*) c zip: (NSString*) z { street = s; city = c; zip = z; /* Take ownership */ ; ; ; return self; }
Now, the program will not crash. You can safely call the print method after the main method has released the NSString objects.
There is one problem. The Address class does not release the NSString objects. Remember the golden rule. The owner of an object should release it. In this example, the NSString objects have two owners – the main method and the Address class. They should both release them. A good place to release these objects is from the destructor method – dealloc. Add this method to the implementation.
- (void) dealloc { ; ; ; ; }
Now, we are all good. The code, in full form will look like this.
#import <Foundation/Foundation.h> @interface Address : NSObject { NSString* street; NSString* city; NSString* zip; } - (Address*) initStreet: (NSString*) s city: (NSString*) c zip: (NSString*) z; - (void) print; @end @implementation Address - (Address*) initStreet: (NSString*) s city: (NSString*) c zip: (NSString*) z { street = s; city = c; zip = z; ; ; ; return self; } - (void) dealloc { puts("Dealloc called"); ; ; ; ; } - (void) print { NSLog(street); NSLog(city); NSLog(zip); } @end int main (int argc, const char * argv[]) { NSString *s = initWithCString: "101 Flora"]; NSString *c = initWithCString: "Miami"]; NSString *z = initWithCString: "33139"]; Address *a = initStreet:s city:c zip:z]; ; ; ; ; ; return 0; }