Chapter    8

Memory Management

This chapter explores how to manage memory with Objective-C.

The recipes in this chapter will show you how to:

  • Understand memory management
  • Use reference counting to manage memory
  • Use Automatic Reference Counting (ARC) to manage memory
  • Use garbage collection (GC) to manage memory on the Mac

8.1 Understanding Memory Management

Problem

You want to understand how to manage memory effectively in your Objective-C applications.

Solution

Memory is one of the finite resources that your Objective-C application requires. Every variable and object that you use in Objective-C takes up some memory. Since memory is a finite resource, it’s possible to use up all the memory that you have available.

Programmers who work in C-based programming languages like Objective-C need to think about how to manage memory effectively. This is something new to many programmers who work in Java or with scripting languages. Managing memory is one of the toughest problems we deal with in Objective-C.

The consequences for applications with mismanaged memory are severe: applications can become slow or the system may even shut down an application that is using too much memory. Applications can also leak memory. Memory leaks are caused when objects have memory assigned to them that doesn’t get reclaimed by the system even though the object can no longer be reached in the application’s code.

Let’s go over some of the things to think about when deciding how to deal with memory management.

Object Lifecycle

Just like objects in the real world, Objective-C objects are created, live, and then go away. This is the object lifecycle.

In terms of memory management, when an object is born, you set aside memory for the object to use. As the object goes on doing what you have programmed it to do, the object continues to require that memory. Once you no longer need the object, you let the object go away. You can reclaim the memory for that object once it has gone away.

Object Ownership

Object ownership is the concept where one entity is responsible for another. When an entity owns an object, the entity is responsible for cleaning the object up when the entity is done with it. For example, if an object was created and used in the main function, then the main function is responsible for the object. This means that it’s the main function’s responsibility to clean up the object.

What makes this concept a bit more complicated is that objects can be owned by more than one entity. So, an object may be created and owned in the main function and also be used by another entity that will claim ownership of the object.

A common situation where you will see multiple object ownership is when you use arrays. Arrays are indexed lists of objects, and when you put an object into an array, the array claims ownership of the object. So, if I create an object in the main function and then put that object into an array, both the main function and the array will claim ownership of the object—and both are responsible for cleaning up the object.

Clearly, keeping track of all the possible object relationships that could happen can quickly become daunting without some kind of system to help. Objective-C does provide a system called reference counting that can help you keep track of object lifecycles and object ownerships.

Reference Counting

Reference counting is a system for keeping track of how many entities are claiming ownership of an object. Each object has a special number called the reference count associated with the object. The reference count represents how many objects claim ownership over the object.

When an object is created, the object gets assigned a reference count of one. This represents the ownership claim of the entity that just created the object. For each entity that claims ownership of the object, the object’s reference count gets increased by one.

Once an entity no longer requires ownership of an object, the entity can decrease the reference count of the object. When an object’s reference count reaches zero, the system will automatically destroy the object and the system will be able to reclaim the memory.

With reference counting, Objective-C takes care of the actual object destruction. Owner objects are only responsible for releasing their claim of ownership on the object.

Autorelease

There are situations where this reference counting system breaks down because it’s unclear who is supposed to claim ownership of an object. For example, some classes provide convenient objects that are meant to be temporary through functions. In such cases, the object creator can’t claim ownership because the creator class will never get the chance to release ownership, and you can’t assume that the receiver will take responsibility of the created object.

Objective-C helps by providing a way to relinquish ownership of an object in a deferred way. So I can create an object and say that at some point in the relatively near future the object can be destroyed. This is called autorelease.

Automatic Reference Counting (ARC)

Automatic Reference Counting, or ARC, is a compiler-level system that automates the process of reference counting. ARC is available starting with iOS 5 and Mac OSX 10.6. with Xcode 4.2 for iOS and Xcode 4.3 for Mac. The recipes in this book have mostly been written using ARC.

ARC essentially automates what you will be doing in this chapter. Before your Objective-C program is compiled, all the code needed for reference counting to work is inserted into your program.

Garbage Collection

Garbage collection is another type of memory management system that is used to automate memory management. This method has the concept of a garbage collector that periodically looks for objects that are no longer being used and then takes them away.

Garbage collection is only available for Mac OSX starting with version 10.5. Note that iOS applications can’t use garbage collection.

In garbage collection, objects can have either a strong or weak reference to a root object. The root object is usually a top-level application object. For example, a Mac application has an object of type NSApplication that is responsible for the application as a whole. Every other object is contained in the NSApplication object, making the NSApplication object the root object. Objects with a strong reference to the root object can be reached by the root object, while objects with a weak reference can’t be reached by the root object.

Periodically, the garbage collection mechanism is activated and any objects with a weak reference are destroyed.

Memory Management Options

With Objective-C, you have three options when you want to implement memory management: manual memory management using reference counting, automatic reference counting (ARC), or garbage collection (excluding iOS applications).

Manual memory management is something that any Objective-C program can use. However, as you’ll see in this chapter, manual memory management is a detailed and time-consuming task.

Automatic Reference Counting is available for Mac and iOS but only for more recent versions of the operating systems. Generally, I recommend using ARC if you’re developing a new application because it’s efficient and it will work on both Mac and iOS. Garbage collection is an option if you are just working on Mac apps.

NOTE: In this chapter, you will see how to do manual memory management as well as garbage collection. ARC isn’t specifically covered since it’s used in the recipes in the other chapters.

8.2 Setting up an Application without ARC

Problem

You want to set up an application that doesn’t use ARC to manage memory.

Solution

When you set up a new Xcode project, you will be presented with a screen that is titled “Choose options for your new project.” One of the options is a checkbox that says “Use Automatic Reference Counting.” Make sure that this option is left unchecked.

You can set up your project like this for a Mac application, Mac command-line applications, or any iOS application. For the purposes of most of the recipes in this chapter (except for the one on garbage collection), I’ll use an iOS application.

How It Works

Xcode sets up your project based on the settings that you provide. When you choose to not use ARC in the options screen, Xcode remembers to compile your project with the compiler setting that indicates to not use ARC. Xcode also allows you to send particular messages required for memory management.

These messages are retain, release, autorelease, and dealloc. If you try to use these messages in an ARC project, you will get a compiler error, but with this non-ARC project you’ll use these messages to implement the reference counting system.

In fact, if you look at the AppDelegate.m file, you will see an example of a dealloc method that Xcode automatically coded for you. This dealloc method includes some memory management code that I’ll cover in the next recipe. See Listings 8-1 through 8-3 for the code.

- (void)dealloc{
    [_window release];
    [super dealloc];
}

The Code

Listing 8-1. main.m

#import <UIKit/UIKit.h>

#import "AppDelegate.h"

int main(int argc, char *argv[]){
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate Image
class]));
    }
}

Listing 8-2. AppDelegate.h

#import <UIKit/UIKit.h>

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;

@end

Listing 8-3. AppDelegate.m

#import "AppDelegate.h"

@implementation AppDelegate

@synthesize window = _window;

- (void)dealloc{
    [_window release];
    [super dealloc];
}

- (BOOL)application:(UIApplication *)application Image
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
    self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] Image
autorelease];
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    return YES;
}

@end

Usage

You use this project like any of the others. For now, just take a look at some of the differences in the app delegate that Xcode created for you. The dealloc method is included here. If you look closely at the constructor for the window property in the application:applicationDidFinishLaunchingWithOptions: method, you can see that the window object was sent an autorelease message. Both of these are examples of what you’ll be doing in the next few recipes for memory management.

8.3 Using Reference Counting to Manage Memory

Problem

You want to use an object in your application and need to make sure that the object’s memory is being managed effectively.

Solution

When an object is created using the messages alloc, new, or copy, the entity where the constructor code is located claims ownership of the object and the object’s reference count is set to one. When the object is no longer needed, the owner is responsible for sending a release message to the object.

How It Works

After you create and use an object, send a release message. For example, if I create an NSObject with alloc and init, I use the object. When I finish, I send the release message to let Objective-C destroy the object.

NSObject *obj = [[NSObject alloc] init];

NSLog(@"obj's description is %@", [obj description]);

[obj release];

In the first line, I use alloc to create an NSObject that has a reference count of one. The reference count is still one when I use the object to write out a message to the log. After the third line of code when I sent the release message, the object’s reference count becomes zero and Objective-C automatically destroys the object.

That’s really all that you need to do. Effective memory management comes down to consistency and absolutely following a series of simple rules. This code reflects the following rule:

RULE: Always match alloc, new, and copy with a release message.

While it’s not really needed in this example, it is possible to increment an object’s reference count. You can do this by sending the retain message to the object. The retain message means that an entity is claiming ownership of an object. If you sent a retain message to obj, you would increase the reference count of obj to two.

NSObject *obj = [[NSObject alloc] init];

NSLog(@"obj's description is %@", [obj description]);

[obj retain];

[obj release];

In effect, you are claiming double ownership of this object. If you just left this code alone, you would have a problem. The object’s reference count starts off as one and then goes to two after the retain message. Then the reference count goes back down to one when you release it. But, if you leave the code as-is, Objective-C will never be able to destroy the object because the reference count never reaches zero.

When this happens, you get a memory leak. As mentioned, memory leaks are caused by objects that use memory and never let the system reclaim the memory. If left unchecked, memory leaks can cause your application to slow down or crash.

To fix this problem, you must send another release message to obj when you’re finished with it.

NSObject *obj = [[NSObject alloc] init];

NSLog(@"obj's description is %@", [obj description]);

[obj retain];

[obj release];

[obj release];

This leads to another memory management rule.

RULE: Always match each retain with a release.

Basically, you want to make sure your object’s reference count is zero when you are finished with the object. See Listing 8-4 for the code.

The Code

Listing 8-4. main.m

#import <UIKit/UIKit.h>

#import "AppDelegate.h"

int main(int argc, char *argv[]){
    @autoreleasepool {

        NSObject *obj = [[NSObject alloc] init];

        NSLog(@"obj's description is %@", [obj description]);

         [obj retain];

         [obj release];

         [obj release];

        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate Image
class]));
    }
}

Usage

You can include the code from Listing 8-4 in your own main function to test it out. By itself, the code doesn’t do much other than write to the log, but you need to follow this pattern whenever you use objects while managing memory manually.

8.4 Adding Memory Management to Your Custom Classes

Problem

You have custom classes that could claim ownership of objects and you want to make sure that they will manage memory correctly.

Solution

There are two major situations where memory management becomes an issue in a custom class: property getter and setter code, and the dealloc method. Properties need to be configured to claim ownership of objects that are assigned to the instance variables that hold the reference to the object. The dealloc method is a special method that Objective-C calls right before an object is destroyed. You need to implement a dealloc method for each custom class.

How It Works

The example used in this recipe expands on the Car class that you coded back in Recipes 1.4 and 1.6. You are going to add the necessary code to this class to make sure that you are following the memory management rules mentioned in Recipe 8.3.

In case you forgot, here is how to use the Car class. Note that I put in a release message this time since you’re not using ARC.

#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import "Car.h"

int main(int argc, char *argv[]){
    @autoreleasepool {

        [Car writeDescriptionToLogWithThisDate:[NSDate date]];

        Car *c = [[Car alloc] init];

        c.name = @"New Car Name";

        [c writeOutThisCarsState];

        [c release];

        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate Image
class]));
    }
}
Strong Property References

The first thing that you need to look at here is the property declaration for name. When you originally coded this, you declared the property like this:

@property(strong) NSString *name;

The strong keyword means that you are going to take ownership of any object that is assigned to the name property here.

NOTE: Before ARC was introduced, you would use the retain keyword here instead of the strong keyword. You can still use retain here if you wish since retain has the same meaning as strong. The reason that retain was used before was because this meant that a retain message was to be sent to the object after it was assigned. You will see both retain and strong.

What you need to do is go back to the getter and setter code and make sure that you are taking ownership of these objects by sending a retain message. Right now you have this setter code in the Car implementation:

-(void)setName:(NSString *)name{
    name_ = name;
}

As you can see, name is not being retained, so you aren’t taking ownership of name. It is possible that you could have a situation where your name property is assigned to you but then gets released by the other object owners. When you attempt to use name, you will get a memory warning and your app will crash.

To fix this problem, you must take ownership of name by sending a retain message to name before assigning name to your local instance name_.

-(void)setName:(NSString *)name{
    [name retain];
    [name_ release];
    name_ = name;
}

As you can see, you also sent a release message to name_ before assigning the new name object to name_. The idea here is that you want to relinquish ownership of any previous name objects you had a claim to and claim ownership of the new name object.

NOTE: You may also use the @synthesize directive here instead of coding your own assessors, and Objective-C will automatically handle the retain and release for you behind the scenes.

The dealloc Method

The dealloc method is called by Objective-C right before an object is destroyed. The purpose of dealloc is to give you a chance to release any objects that you have ownership of before the owner object is destroyed. You need to override the dealloc method for each custom class that you create. You release each of your local instances and then set them to nil.

Here is how you could code a dealloc method for your Car class:

-(void)dealloc{
    NSLog(@"%@'s dealloc is executing", self.name);
    [super dealloc];
    [name_ release];
    name_ = nil;
}

As you can see, you must send the dealloc method to the superclass first because anything that your parent object is holding on to must be released as well. Here it’s very important to release any objects that are marked with a strong reference. You should also set any object to nil here. See Listings 8-5 through 8-7 for the code.

The Code

Listing 8-5. Car.h

#import <Foundation/Foundation.h>

@interface Car : NSObject{
@private
    NSString *name_;
}

@property(strong) NSString *name;

+(void)writeDescriptionToLogWithThisDate:(NSDate *)date;

-(void)writeOutThisCarsState;

@end

Listing 8-6. Car.m

#import "Car.h"

@implementation Car

-(void)setName:(NSString *)name{
    [name retain];
    [name_ release];
    name_ = name;
}

-(NSString *) name{
    return name_;
}

+(void)writeDescriptionToLogWithThisDate:(NSDate *)date{
        NSLog(@"Today's date is %@ and this class represents a car", date);
}

-(void)writeOutThisCarsState{
        NSLog(@"This car is a %@", self.name);
}

-(void)dealloc{
    NSLog(@"%@'s dealloc is executing", self.name);
    [super dealloc];
    [name_ release];
    name_ = nil;
}

@end

Listing 8-7. main.m

#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import "Car.h"

int main(int argc, char *argv[]){
    @autoreleasepool {

        [Car writeDescriptionToLogWithThisDate:[NSDate date]];

        Car *c = [[Car alloc] init];

        c.name = @"New Car Name";

        [c writeOutThisCarsState];

        [c release];

        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate Image
class]));
    }
}

Usage

To use this code, add the Car class from the listings to your Xcode project. You can add the code that creates the Car objects right into your own main.m file to test out this recipe for yourself. Build and run the project to see the results of the program. You can see when the dealloc has executed if you examine the log.

This car is a New Car Name
New Car Name's dealloc is executing

If you comment out the release message that you sent to c in the main.m file and re-run your Xcode project, you’ll see that dealloc was never reached and that the objects in c were never released.

8.5 Using Autorelease

Problem

You want to return an object that was created with an alloc, new, or copy from a function on a custom class. You can’t release the object before you return the object to the caller because if the caller attempts to use the object, it will get a memory error and the application will crash. If you don’t release the object, you violate the first memory management rule and risk causing a memory leak.

Solution

Before returning an object to a caller, send the autorelease message to the object. This is a way to send a deferred release message to an object. The idea is that you don’t want the object destroyed immediately because the caller needs the object temporarily, but you do want the object to eventually be released and destroyed. Or you may want the caller to claim ownership of the object by sending a retain message.

How It Works

Autorelease is used in Foundation objects that provide functions that return objects but without the usual alloc and init messages. For instance, NSDate has a function date that returns an NSDate object populated with the current date and time. Although you can see this for yourself, in the NSDate date function code an autorelease message is sent to the object before being returned to you.

NSDate *today = [NSDate date];
NSLog(@"Today's date is %@", today);

You can use this object and you don’t need to worry about releasing the object because you never used the alloc, new, or copy functions to create the today object. For your purposes, the reference count is zero and you are not violating the first memory management rule. But you also can’t assume that this today object will not be destroyed sometime in the future unless you claim ownership.

If you wanted to claim ownership of the today object, you could send a retain message to the today object. This would make the reference count of date equal to one and the object would remain with you even when the deferred release message is sent to the object.

For example, if you want to ensure that today would remain for a bit longer, you could do something like this:

NSDate *today = [NSDate date];
NSLog(@"Today's date is %@", today);
[today retain];

//do other things...

[today release];

Now you know that you can use the today object until you’re finished. And you’re staying in line with the second memory management rule.

When you create functions like the NSDate date function, you also need to use autorelease. To see how to use autorelease, add a function to the Car class from Recipe 8.4. What you want to do is code a function that will return a Car object to the caller based on a name parameter that the caller provides.

The first thing you need to do is provide a forward declaration in the Car.h file.

+(Car *)carWithThisName:(NSString *)carName;

You can see that this is a class function because the function starts off with a plus sign. Also, the class Car is specified as the return type, so you know that this function will be returning a Car object. You also have a parameter to allow the caller to specify the name for the Car object that they will get back.

When you move on to the implementation, your first instinct might be to create a new Car object with alloc and init and assign the name property before returning the new object to the caller. Here’s what might end up in the implementation (Car.m):

+(Car *)carWithThisName:(NSString *)carName{
    Car *car = [[Car alloc] init];
    car.name = carName;

    return car;
}

What you’re doing is creating a new object and claiming ownership over the object when you use the alloc message. Then you return the object without releasing the object, so you’re violating the first rule of memory management. Your caller has no way of knowing that the returned object has a reference count of one, so right now you would end up with a memory leak.

To fix this, all you need to do is send the autorelease message before returning the object to the caller.

+(Car *)carWithThisName:(NSString *)carName{
    Car *car = [[Car alloc] init];
    car.name = carName;
    [car autorelease];

    return car;
}

This gives the Car a deferred release message, which makes Car a temporary object.

This example also implies yet another memory management rule.

RULE: Always send an autorelease message to objects in functions before returning them to callers.

There is a complementary rule that goes along with this one.

RULE: Always assume that objects returned from functions are autoreleased and therefore temporary unless retained.

Here is how you could use the Car object:

Car *tempCar = [Car carWithThisName:@"Temporary Car"];

[tempCar writeOutThisCarsState];

See Listings 8-8 through 8-12 for the code.

The Code

Listing 8-8. Car.h

#import <Foundation/Foundation.h>

@interface Car : NSObject{
@private
    NSString *name_;
}

@property(strong) NSString *name;

+(void)writeDescriptionToLogWithThisDate:(NSDate *)date;

-(void)writeOutThisCarsState;

+(Car *)carWithThisName:(NSString *)carName;

@end

Listing 8-9. Car.m

#import "Car.h"

@implementation Car

-(void)setName:(NSString *)name{
    [name retain];
    [name_ release];
    name_ = name;
}

-(NSString *) name{
    return name_;
}

+(void)writeDescriptionToLogWithThisDate:(NSDate *)date{
        NSLog(@"Today's date is %@ and this class represents a car", date);
}

-(void)writeOutThisCarsState{
        NSLog(@"This car is a %@", self.name);
}

-(void)dealloc{
    NSLog(@"%@'s dealloc is executing", self.name);
    [super dealloc];
    [name_ release];
    name_ = nil;
}

+(Car *)carWithThisName:(NSString *)carName{
    Car *car = [[Car alloc] init];
    car.name = carName;
    [car autorelease];

    return car;
}

@end

Listing 8-10. AppDelegate.h

#import <UIKit/UIKit.h>
#import "Car.h"

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;

@end

Listing 8-11. AppDelegate.m

#import "AppDelegate.h"

@implementation AppDelegate

@synthesize window = _window;

- (void)dealloc{
    [_window release];
    [super dealloc];
}

- (BOOL)application:(UIApplication *)application Image
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{

    Car *tempCar = [Car carWithThisName:@"Temporary Car"];

    [tempCar writeOutThisCarsState];

    self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] Image
autorelease];
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    return YES;
}

@end

Listing 8-12. main.m

#import <UIKit/UIKit.h>
#import "AppDelegate.h"

int main(int argc, char *argv[])
{
    @autoreleasepool {
        NSDate *today = [NSDate date];
        NSLog(@"Today's date is %@", today);
        [today retain];

        //do other things...

        [today release];

        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate Image
class]));
    }
}

Usage

Add the Car.h and Car.m files to your Xcode project and then include the same code that I did in the app delegation’s application:didFinishLaunchingWithOptions: method. Remember that the dealloc method that you coded in Recipe 8.4 writes out a message to the log right before Objective-C destroys a Car object. So you can use this to test if sending the autorelease message really lets the object be destroyed.

When this application runs, you will see the message from the dealloc printed out to the log:

Today's date is 2012-06-27 14:29:21 +0000
This car is a Temporary Car
Applications are expected to have a root view controller at the end of application Image
launch
Temporary Car's dealloc is executing

You can see that the Car object’s state was written out and that the dealloc executed, even though you never sent a release message.

8.6 Enabling Garbage Collection for Mac Applications

Problem

You want to enable garbage collection on your Mac application as an alternative to manual memory management or ARC.

Solution

Enable garbage collection by changing your Xcode project’s Objective-C Garbage Collection Setting build setting to either Supported (-fobjc–gc) or Required (-fobjc-gc-only).

How It Works

To enable garbage collection in your Mac application, select your project and your target (see Figure 8-1 for a reference). Then choose the Build Settings tab and look for the Objective-C Garbage Collection build setting. You can use the search box in the upper right hand corner to find this setting more easily. Choose the option Supported (-fobjc-gc) in both spots.

Image

Figure 8-1. Setting the Objective-C Garbage Collection build setting

You may also choose the Required (-fobjc-gc-only) option here. The Support option allows you to mix manual memory management with garbage collected memory management while the Required option forces you to use only garbage collection. Once you do this, you can write your code without worrying about manual memory management.

That's all you need to know to get started using garbage collection on the Mac. For the most part, you can treat your Mac applications that are using garbage collection like you would your ARC apps. That is, you can stop worrying about manual memory management.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.117.188.138