!!!Obj-c on Mac --- Chapter 9 Memory Management

来源:互联网 发布:网络发彩信 编辑:程序博客网 时间:2024/06/06 18:41

Object Life Cycle

Reference Counting

Every object has an integer associated with it, known as its reference count orretain count.

When an object is created via alloc or new, or via a copy message (which makes a copy of the receiving object), the object’s retain count is set to 1. To increase its retain count, send the object a retain message. To decrease its retain count, send the object a release message.

When an object is about to be destroyed because its retain count has reached 0, Objective- C will automatically send the object a dealloc message. You can override dealloc in your objects. Do this to release any related resources you might have allocated. Don’t ever call dealloc directly.

To find out the current retain count, send the retainCount message.

- (id) retain;- (void) release;- (unsigned) retainCount;
Retain returns an id. That way, you can chain a retain call with other message sends, incrementing its retain count and then asking it to do some work. For instance, [[car retain]
setTire: tire atIndex: 2]; asks car to bump up its retain count and perform the setTire action.

Object Ownership

An object with instance variables that point to other objects is said to own those other objects. Similarly, a function that creates an object is said to own that object.

Retaining and Releasing in Accessors

Bad code 1:

- (void) setEngine: (Engine *) newEngine{    engine = [newEngine retain];    // BAD CODE: do not steal. See fixed version below.} // setEngine
Problem:

Engine *engine1 = [Engine new]; // count: 1[car setEngine: engine1]; // count: 2[engine1 release]; // count: 1Engine *engine2 = [Engine new]; // count: 1[car setEngine: engine2]; // count: 2

Bad code 2:

- (void) setEngine: (Engine *) newEngine{    [engine release];    engine = [newEngine retain];    // More BAD CODE: do not steal. Fixed version below.} // setEngine
Problem:

Engine *engine = [Engine new]; // count: 1Car *car1 = [Car new];Car *car2 = [Car new];[car1 setEngine: engine]; // count: 2[engine release]; // count 1[car2 setEngine: [car1 engine]]; // oops!
Good code:
- (void) setEngine: (Engine *) newEngine{[newEngine retain];[engine release];engine = newEngine;} // setEngine

Autorelease

Cocoa has the concept of the autorelease pool. It’s a pool (collection) of stuff, presumably objects, that automatically get released.

NSObject provides a method called autorelease:

- (id) autorelease;
This method schedules a release message to be sent at some time in the future. The return value is the object that receives the message; retain uses this same technique, which
makes chaining calls together easy. What actually happens when you send autorelease to an object is that the object is added to an NSAutoreleasePool. When that pool is destroyed, all the objects in the pool are sent a release message.
- (NSString *) description{NSString *description;description = [[NSString alloc]initWithFormat: @"I am %d years old", 4];return ([description autorelease]);} // description
In the Foundation tools we’ve been using, the creation and destruction of the pool has been explicit:
NSAutoreleasePool *pool;pool = [[NSAutoreleasePool alloc] init];...[pool release];
When you create an autorelease pool, it automatically becomes the active pool. When you release that pool, its retain count goes to 0, so it then gets deallocated. During the deallocation, it releases all the objects it has.
You may have seen in Xcode’s autogenerated code an alternate way of destroying an autorelease pool’s objects: the -drain method. This method empties out the pool without destroying it. -drain is only available in Mac OS X 10.4 (Tiger) and later. In our own code (not generated by Xcode), we’ll be using -release, since that will work on versions of the OS back to the beginning of time.

The Rules of Cocoa Memory Management

■ When you create an object using new, alloc, or copy (for example,alloc,newObject, or mutableCopy), the object has a retain count of 1. You are responsible for sending the object a release or autorelease message when you’re done with it. That way, it gets cleaned up when its useful life is over.
■ When you get hold of an object via any other mechanism, assume it has a retain count of 1 and that it has already been autoreleased. You don’t need to do any further work to make sure it gets cleaned up. If you’re going to hang on to the object for any length of time, retain it and make sure to release it when you’re done.
■ If you retain an object, you need to (eventually) release or autorelease it. Balance these retains and releases.

If you get the object from new, alloc, or copy, you need to arrange its demise, usually with a release:

NSMutableArray *array;array = [[NSMutableArray alloc] init]; // count: 1// use the array[array release]; // count: 0
If you get the object from any other mechanism, such as arrayWithCapacity:, you don’t have to worry about destroying it:
NSMutableArray *array;array = [NSMutabelArray arrayWithCapacity: 17];// count: 1, autoreleased// use the array
arrayWithCapacity: is not alloc, new, or copy, so you can assume that the object being returned has a retain count of 1 and has already been autoreleased. When the autorelease pool goes away, array is sent the release message, its retain count goes to 0, and its memory is recycled.
NSColor *color;color = [NSColor blueColor];// use the color
blueColor is not alloc, new, or copy, so you can assume it has a retain count of 1 and is autoreleased. blueColor returns a global singleton object—a single object that’s shared by every program that needs it—and won’t actually ever get destroyed.

Hanging on to Objects

If you’re getting an object from init, new, or copy, you don’t need to do anything special. The object’s retain count will be 1, so it will stick around. Just be sure to release the object in the dealloc method of the owner- object that’s hanging on to it:

- (void) doStuff{// flonkArray is an instance variableflonkArray = [NSMutableArray new]; // count: 1} // doStuff- (void) dealloc{[flonkArray release]; // count: 0[super dealloc];} // dealloc
If you get an object from something other than init, new, or copy, you need to remember to retain it.


event loop

A typical graphical application spends a lot of time waiting on the user to do something. The program sits twiddling its thumbs until the very slow human at the controls decides to click the mouse or press a key. When one of these events does happen, the program wakes up and gets to work doing whatever is necessary to respond to the event. After the event is handled, the application goes back to sleep waiting for the next event.

To keep your program’s memory footprint low, Cocoa creates an autorelease pool before it starts handling the event and destroys the pool after the event is handled. This keeps the amount of accumulated temporary objects to a minimum.

- (void) doStuff{  // flonkArray is an instance variable  flonkArray = [NSMutableArray arrayWithCapacity: 17];  // count: 1, autoreleased  [flonkArray retain]; // count: 2, 1 autorelease} // doStuff- (void) dealloc{  [flonkArray release]; // count: 0  [super dealloc];} // dealloc

KEEPING THE POOL CLEAN

The autorelease pool is purged at well- defined times: when it’s explicitly destroyed in your own code or at the end of the event loop when using the AppKit. You don’t have to worry about a demon that goes around destroying autorelease pools at random. You also don’t have to retain each and every object you use, because the pool won’t go away in the middle of a function.

Sometimes autorelease pools don’t get cleaned out as often as you would like.

int i;for (i = 0; i < 1000000; i++) {id object = [someArray objectAtIndex: i];NSString *desc = [object description];// and do something with the description}
The way to work around this is to create your own autorelease pool inside the loop.

NSAutoreleasePool *pool;pool = [[NSAutoreleasePool alloc] init];int i;for (i = 0; i < 1000000; i++) {id object = [someArray objectAtIndex: i];NSString *desc = [object descrption];// and do something with the descriptionif (i % 1000 == 0) {[pool release];pool = [[NSAutoreleasePool alloc] init];}}[pool release]

Garbage Collector

Objective-C 2.0 introduces automatic memory management, also called garbage collection.

The Objective- C garbage collector is a generational garbage collector. Newly created objects are much more likely to turn into garbage than objects that have been hanging around for awhile. At regular times, the garbage collector starts looking at your variables and objects and follows the pointers between them. Any object it discovers without anything pointing to it is garbage, which is fit to be thrown away. The worst thing you can do is keep a pointer to an object that you’re done with. So if you point to an object in an instance variable (recall composition), be sure to assign nil to your instance variable, which removes your reference to this object and lets the garbage collector know it can be purged. Like the autorelease pool, garbage collection is triggered at the end of an event loop. You can also trigger garbage collection yourself if you’re not in a GUI program


0 0
原创粉丝点击