Question

I'm new to Objective-C and cocoa. In the guide provided by Apple for Cocoa, there is a confusing example in memory management:

Suppose you want to implement a method to reset the counter. You have a couple of choices. The first implementation creates the NSNumber instance with alloc, so you balance that with a release.

- (void)reset {
    NSNumber *zero = [[NSNumber alloc] initWithInteger:0];
    [self setCount:zero];
    [zero release];
}

The second uses a convenience constructor to create a new NSNumber object. There is therefore no need for retain or release messages

- (void)reset {
    NSNumber *zero = [NSNumber numberWithInteger:0];
    [self setCount:zero];
}

I am not sure why the object is created with 'new' instead of 'alloc & init' does not need to be retained/released. My understanding is that both are doing the same thing, except that with 'alloc & init' we can use custom checks and initialisation.

Many thanks.

Was it helpful?

Solution

The second example returns an auto released object. The code for the convenience constructor probably looks like this, at least when it comes to replicate the functionality.

+ (NSNumber *)numberWithInteger:(NSInteger)integer
{
    NSNumber *number = [[NSNumber alloc] initWithInteger:integer];
    return [number autorelease];
}

Autorelease is a way to defer sending a release method to an object while not delegating ownership of the object to the caller of the constructor. This is an important concept, because the naming conventions require you to not return ownership of an object unless your method starts with copy new alloc or retain. However, since you can't return an owned object, you would have to call release on it in your convenience constructor, which would then lead to you returning a deallocated object. So, autorelease allows you to return an un-owned object to the caller which will receive an actual release method later (when the current autorelease pool gets drained).

Autorelease methods are collected in so called autorelease pools, which are thread local quasi linked lists (they are not implemented as linked lists, but they do work like they were) and that just collect autoreleased objects. Objects can be added to them multiple times by calling, once for each autorelease method they receive. When the pools is drained or destroyed, all objects it contains will receive a release message. By default, the system will provide you with an auto release pool, at least on the main thread, but you can create your own ones using this code (which is also used in every main method, if you take a look):

@autoreleaspool
{
    [foo autorelease]; // foo will receive a `release` method at the closing brace    
}

OTHER TIPS

By convention, all methods who's name start with "alloc" or "copy" or "new" increase the retain count by 1.

All other methods do not change the retain count. Since "numberWithInteger" doesn't start with "alloc" or "copy" or "new" it returns an object with a retain count of 0. But instead of setting it to 0 immediately (which would never work), it sets the retain count to 1 and then schedules it to be dropped down to 0 at some point in the future (usually when the event loop is idle, but you can manually make it happen sooner), using an "autorelease pool".

The only exception to the alloc/copy/new naming convention is, of course, the actual "retain" and "release" and "autorelease" methods, which increase/decrease the retain count or schedule it to be decreased later.

It is not a language feature, it's just a coding practice. Every method starting with "alloc" and "copy" and "new" sets retain to 1, everything else sets it to 1 but schedules it to be dropped to 0 later.

Of course, if you are using ARC, and you should absolutely be using ARC, then you don't have to worry about any of this because the compiler will find the last line of code that uses the object, and will insert a line of code after it to free up the memory. This leads to faster code, because the autorelease mechanism is a bit slow and sometimes uses too much RAM.

When ARC is enabled, none of those rules apply. The compiler ignores all your memory management code (or tells you to delete it), and writes it's own code. It's not actual garbage collection, it's just manual memory management that is generated by Xcode instead of written by hand.

Objects that are created with +alloc -init are returned with a retain count of +1, meaning that you have to call -release when you are done with this object. This is the same with +new , which is just a simple combination of [[ alloc] init], eg.

NSDate *date = [NSDate new]; // I will have to send -release at some point

As a convention, methods that contains init, new, create and copy will transfer ownership to you, meaning that you need to send -release at some point. Other methods will not transfer ownership, meaning that the object is autoreleased, ie. it has a short lifetime and is scheduled to be dealloced in the future. If you want this object to stick around, you will need to explicitly take ownership by sending -retain.

NSDate *date = [NSDate date]; // I won't have to send -release
// But this object will be dealloced soon
// so if I want it to stick around, I will need to retain it

This convention is not enforced by the language.

It is also useful to remember that this convention expands beyond the Objective-C part of the SDK, it is also the case in CoreGraphics and all C frameworks provided by Apple, and most third-party frameworks make use of this convention.

CGContextRef context = UIGraphicsGetCurrentContext(); // I won't have to release this context
CGImageRef result = CGBitmapContextCreateImage(context); // I will have to release this image
/* ... */
CGRelease(result);

As for your example : -numberWithInteger: does not contain either init, new, create or copy. So you don't need to -release it when done.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top