Understanding reference counting with Cocoa and Objective-C
Asked Answered
L

15

125

I'm just beginning to have a look at Objective-C and Cocoa with a view to playing with the iPhone SDK. I'm reasonably comfortable with C's malloc and free concept, but Cocoa's references counting scheme has me rather confused. I'm told it's very elegant once you understand it, but I'm just not over the hump yet.

How do release, retain and autorelease work and what are the conventions about their use?

(Or failing that, what did you read which helped you get it?)

Longinus answered 9/8, 2008 at 3:13 Comment(0)
P
151

Let's start with retain and release; autorelease is really just a special case once you understand the basic concepts.

In Cocoa, each object keeps track of how many times it is being referenced (specifically, the NSObject base class implements this). By calling retain on an object, you are telling it that you want to up its reference count by one. By calling release, you tell the object you are letting go of it, and its reference count is decremented. If, after calling release, the reference count is now zero, then that object's memory is freed by the system.

The basic way this differs from malloc and free is that any given object doesn't need to worry about other parts of the system crashing because you've freed memory they were using. Assuming everyone is playing along and retaining/releasing according to the rules, when one piece of code retains and then releases the object, any other piece of code also referencing the object will be unaffected.

What can sometimes be confusing is knowing the circumstances under which you should call retain and release. My general rule of thumb is that if I want to hang on to an object for some length of time (if it's a member variable in a class, for instance), then I need to make sure the object's reference count knows about me. As described above, an object's reference count is incremented by calling retain. By convention, it is also incremented (set to 1, really) when the object is created with an "init" method. In either of these cases, it is my responsibility to call release on the object when I'm done with it. If I don't, there will be a memory leak.

Example of object creation:

NSString* s = [[NSString alloc] init];  // Ref count is 1
[s retain];                             // Ref count is 2 - silly
                                        //   to do this after init
[s release];                            // Ref count is back to 1
[s release];                            // Ref count is 0, object is freed

Now for autorelease. Autorelease is used as a convenient (and sometimes necessary) way to tell the system to free this object up after a little while. From a plumbing perspective, when autorelease is called, the current thread's NSAutoreleasePool is alerted of the call. The NSAutoreleasePool now knows that once it gets an opportunity (after the current iteration of the event loop), it can call release on the object. From our perspective as programmers, it takes care of calling release for us, so we don't have to (and in fact, we shouldn't).

What's important to note is that (again, by convention) all object creation class methods return an autoreleased object. For example, in the following example, the variable "s" has a reference count of 1, but after the event loop completes, it will be destroyed.

NSString* s = [NSString stringWithString:@"Hello World"];

If you want to hang onto that string, you'd need to call retain explicitly, and then explicitly release it when you're done.

Consider the following (very contrived) bit of code, and you'll see a situation where autorelease is required:

- (NSString*)createHelloWorldString
{
    NSString* s = [[NSString alloc] initWithString:@"Hello World"];

    // Now what?  We want to return s, but we've upped its reference count.
    // The caller shouldn't be responsible for releasing it, since we're the
    // ones that created it.  If we call release, however, the reference 
    // count will hit zero and bad memory will be returned to the caller.  
    // The answer is to call autorelease before returning the string.  By 
    // explicitly calling autorelease, we pass the responsibility for
    // releasing the string on to the thread's NSAutoreleasePool, which will
    // happen at some later time.  The consequence is that the returned string 
    // will still be valid for the caller of this function.
    return [s autorelease];
}

I realize all of this is a bit confusing - at some point, though, it will click. Here are a few references to get you going:

  • Apple's introduction to memory management.
  • Cocoa Programming for Mac OS X (4th Edition), by Aaron Hillegas - a very well written book with lots of great examples. It reads like a tutorial.
  • If you're truly diving in, you could head to Big Nerd Ranch. This is a training facility run by Aaron Hillegas - the author of the book mentioned above. I attended the Intro to Cocoa course there several years ago, and it was a great way to learn.
Prosy answered 9/8, 2008 at 4:40 Comment(10)
You wrote: "By calling autorelease, we temporarily bump the reference count". I think this is wrong; autorelease only marks the object to be released in the future, it doesn't increase the ref count: cocoadev.com/index.pl?AutoReleaseSkantze
"Now for autorelease. Autorelease is used as a convenient (and sometimes necessary) way to tell the system to free this object up after a little while." As a lead-in sentence, this is wrong. It doesn't tell the system to "free [it] up", it tells it to decrement the retain count.Libenson
Similarly, your last code example releases the NSString one too many times--once in the NSAutorelease pool via autorelease and directly via release :(Zigmund
You are absolutely right, thanks. I'm not sure how I missed that with my last edit.Prosy
It's not the init instance method that increases the retain count, but the alloc class method. Apple's rules are linked below, but the summary is "If you alloc, retain, or copy it, it's your job to release it. Otherwise it isn't." -- stepwise.com/Articles/Technical/2001-03-11.01.html Apple's docs: developer.apple.com/documentation/Cocoa/Conceptual/MemoryMgmt/…Neiman
Your example isn't well chosen. In Core Foundation objects built with a CFClassCreateInstance method have a retain count of 1 that has to be released by the calling code.Hoedown
Thanks a lot for the good explanation. Just one thing that is still unclear. If NSString* s = [[NSString alloc] initWithString:@"Hello World"]; returns an autoreleased object (as you write it) why do I have to do a return [s autorelease]; and set it "autorelease" again and not just return s?Carolus
@Stefan: [[NSString alloc] initWithString:@"Hello World"] will NOT return an autoreleased object. Whenever alloc is called, the reference count is set to 1, and it is the responsibility of that code to make sure it gets released. The [NSString stringWithString:] call, on the other hand, does return an autoreleased object.Prosy
Sorry, my fault. You're totally right. I somehow read stringWithString from the above example and didn't look closely enough to notice that it is an initWithString.Carolus
Fun trivia: Since the answer uses @"" and NSString, the strings are constant throughout and, thus, the absolute retain count will be both constant and entirely irrelevant.... doesn't make the answer wrong, by any means, just reinforces the fact that absolute retain counts are never really something you should worry about.Unexpressed
P
10

If you understand the process of retain/release then there are two golden rules that are "duh" obvious to established Cocoa programmers, but unfortunately are rarely spelled out this clearly for newcomers.

  1. If a function which returns an object has alloc, create or copy in its name then the object is yours. You must call [object release] when you are finished with it. Or CFRelease(object), if it's a Core-Foundation object.

  2. If it does NOT have one of these words in its name then the object belongs to someone else. You must call [object retain] if you wish to keep the object after the end of your function.

You would be well served to also follow this convention in functions you create yourself.

(Nitpickers: Yes, there are unfortunately a few API calls that are exceptions to these rules but they are rare).

Pettigrew answered 11/8, 2008 at 18:42 Comment(4)
This is incomplete and inaccurate. I continue to fail to understand why people try to repeat the rules rather than simply pointing to the relevant documentation: developer.apple.com/documentation/Cocoa/Conceptual/MemoryMgmt/…Libenson
The Core Foundation rules in particular are different from those of Cocoa; see developer.apple.com/documentation/CoreFoundation/Conceptual/…Libenson
I also disagree. If a function is returning something it doesn't want to own, it should autorelease it. It is the caller of the functions job to retain it (if desired). It should have NOTHING to do with the name of any method being invoked. That is more C Style coding where ownership of objects is unclear.Kraken
Sorry! I think I was hasty in down-voting. Memory Management Rules Your answer nearly quotes the apple doc.Kraken
A
8

If you're writing code for the desktop and you can target Mac OS X 10.5, you should at least look into using Objective-C garbage collection. It really will simplify most of your development — that's why Apple put all the effort into creating it in the first place, and making it perform well.

As for the memory management rules when not using GC:

  • If you create a new object using +alloc/+allocWithZone:, +new, -copy or -mutableCopy or if you -retain an object, you are taking ownership of it and must ensure it is sent -release.
  • If you receive an object in any other way, you are not the owner of it and should not ensure it is sent -release.
  • If you want to make sure an object is sent -release you can either send that yourself, or you can send the object -autorelease and the current autorelease pool will send it -release (once per received -autorelease) when the pool is drained.

Typically -autorelease is used as a way of ensuring that objects live for the length of the current event, but are cleaned up afterwards, as there is an autorelease pool that surrounds Cocoa's event processing. In Cocoa, it is far more common to return objects to a caller that are autoreleased than it is to return objets that the caller itself needs to release.

Articulation answered 9/8, 2008 at 22:27 Comment(0)
T
6

Objective-C uses Reference Counting, which means each Object has a reference count. When an object is created, it has a reference count of "1". Simply speaking, when an object is referred to (ie, stored somewhere), it gets "retained" which means its reference count is increased by one. When an object is no longer needed, it is "released" which means its reference count is decreased by one.

When an object's reference count is 0, the object is freed. This is basic reference counting.

For some languages, references are automatically increased and decreased, but objective-c is not one of those languages. Thus the programmer is responsible for retaining and releasing.

A typical way to write a method is:

id myVar = [someObject someMessage];
.... do something ....;
[myVar release];
return someValue;

The problem of needing to remember to release any acquired resources inside of code is both tedious and error-prone. Objective-C introduces another concept aimed at making this much easier: Autorelease Pools. Autorelease pools are special objects that are installed on each thread. They are a fairly simple class, if you look up NSAutoreleasePool.

When an object gets an "autorelease" message sent to it, the object will look for any autorelease pools sitting on the stack for this current thread. It will add the object to the list as an object to send a "release" message to at some point in the future, which is generally when the pool itself is released.

Taking the code above, you can rewrite it to be shorter and easier to read by saying:

id myVar = [[someObject someMessage] autorelease];
... do something ...;
return someValue;

Because the object is autoreleased, we no longer need to explicitly call "release" on it. This is because we know some autorelease pool will do it for us later.

Hopefully this helps. The Wikipedia article is pretty good about reference counting. More information about autorelease pools can be found here. Also note that if you are building for Mac OS X 10.5 and later, you can tell Xcode to build with garbage collection enabled, allowing you to completely ignore retain/release/autorelease.

Tired answered 9/8, 2008 at 3:48 Comment(1)
This is just wrong. There is no need to send someObject release or autorlease in either of the examples shown.Libenson
L
6

Joshua (#6591) - The Garbage collection stuff in Mac OS X 10.5 seems pretty cool, but isn't available for the iPhone (or if you want your app to run on pre-10.5 versions of Mac OS X).

Also, if you're writing a library or something that might be reused, using the GC mode locks anyone using the code into also using the GC mode, so as I understand it, anyone trying to write widely reusable code tends to go for managing memory manually.

Longinus answered 9/8, 2008 at 3:59 Comment(1)
It is perfectly possible to write a hybrid framework that supports both GC and reference counting.Libenson
L
6

As ever, when people start trying to re-word the reference material they almost invariably get something wrong or provide an incomplete description.

Apple provides a complete description of Cocoa's memory management system in Memory Management Programming Guide for Cocoa, at the end of which there is a brief but accurate summary of the Memory Management Rules.

Libenson answered 20/10, 2008 at 0:42 Comment(3)
Try: developer.apple.com/documentation/Cocoa/Conceptual/MemoryMgmt/…Neiman
And for the summary rules: developer.apple.com/documentation/Cocoa/Conceptual/MemoryMgmt/…Neiman
Actually this is a much better single-page summary: developer.apple.com/mac/library/documentation/Cocoa/Conceptual/…Papillote
B
6

I'll not add to the specific of retain/release other than you might want to think about dropping $50 and getting the Hillegass book, but I would strongly suggest getting into using the Instruments tools very early in the development of your application (even your first one!). To do so, Run->Start with performance tools. I'd start with Leaks which is just one of many of the instruments available but will help to show you when you've forgot to release. It's quit daunting how much information you'll be presented with. But check out this tutorial to get up and going fast:
COCOA TUTORIAL: FIXING MEMORY LEAKS WITH INSTRUMENTS

Actually trying to force leaks might be a better way of, in turn, learning how to prevent them! Good luck ;)

Beryl answered 19/12, 2008 at 20:4 Comment(0)
T
5

Matt Dillard wrote:

return [[s autorelease] release];

Autorelease does not retain the object. Autorelease simply puts it in queue to be released later. You do not want to have a release statement there.

Tired answered 9/8, 2008 at 13:48 Comment(0)
U
5

My usual collection of Cocoa memory management articles:

cocoa memory management

Unhandled answered 10/1, 2009 at 10:59 Comment(0)
C
4

NilObject's answer is a good start. Here's some supplemental info pertaining to manual memory management (required on the iPhone).

If you personally alloc/init an object, it comes with a reference count of 1. You are responsible for cleaning up after it when it's no longer needed, either by calling [foo release] or [foo autorelease]. release cleans it up right away, whereas autorelease adds the object to the autorelease pool, which will automatically release it at a later time.

autorelease is primarily for when you have a method that needs to return the object in question (so you can't manually release it, else you'll be returning a nil object) but you don't want to hold on to it, either.

If you acquire an object where you did not call alloc/init to get it -- for example:

foo = [NSString stringWithString:@"hello"];

but you want to hang on to this object, you need to call [foo retain]. Otherwise, it's possible it will get autoreleased and you'll be holding on to a nil reference (as it would in the above stringWithString example). When you no longer need it, call [foo release].

Cockerham answered 9/8, 2008 at 4:25 Comment(0)
C
4

There's a free screencast available from the iDeveloperTV Network

Memory Management in Objective-C

Concoct answered 8/12, 2008 at 11:0 Comment(1)
Unfortunately this link is now a 404.Dilator
C
2

The answers above give clear restatements of what the documentation says; the problem most new people run into is the undocumented cases. For example:

  • Autorelease: docs say it will trigger a release "at some point in the future." WHEN?! Basically, you can count on the object being around until you exit your code back into the system event loop. The system MAY release the object any time after the current event cycle. (I think Matt said that, earlier.)

  • Static strings: NSString *foo = @"bar"; -- do you have to retain or release that? No. How about

    -(void)getBar {
        return @"bar";
    }
    

    ...

    NSString *foo = [self getBar]; // still no need to retain or release
    
  • The Creation Rule: If you created it, you own it, and are expected to release it.

In general, the way new Cocoa programmers get messed up is by not understanding which routines return an object with a retainCount > 0.

Here is a snippet from Very Simple Rules For Memory Management In Cocoa:

Retention Count rules

  • Within a given block, the use of -copy, -alloc and -retain should equal the use of -release and -autorelease.
  • Objects created using convenience constructors (e.g. NSString's stringWithString) are considered autoreleased.
  • Implement a -dealloc method to release the instancevariables you own

The 1st bullet says: if you called alloc (or new fooCopy), you need to call release on that object.

The 2nd bullet says: if you use a convenience constructor and you need the object to hang around (as with an image to be drawn later), you need to retain (and then later release) it.

The 3rd should be self-explanatory.

Chengtu answered 6/11, 2008 at 3:55 Comment(4)
"Autorelease: docs say it will trigger a release "at some point in the future." WHEN?!" The docs are clear on that point: "autorelease just means “send a release message later” (for some definition of later—see “Autorelease Pools”)." Exacely when depends on the autorelease pool stack...Libenson
... "The system MAY release the object any time after the current event cycle." This makes the system sound rather less deterministic than it is...Libenson
... NSString foo = [self getBar]; // still no need to retain or release This is wrong. Whoever invokes getBar doesn't know the implementation details, so *should retain/release (typically via accessors) if they want to use it outside of the current scope.Libenson
The "Very Simple Rules For Memory Management In Cocoa" article is in several respects out of date -- in particular "Objects created using convenience constructors (e.g. NSString's stringWithString) are considered autoreleased." is not right -- it is simply "not owned by the recipient".Libenson
S
1

Lots of good information on cocoadev too:

Spiky answered 15/9, 2008 at 20:46 Comment(0)
P
0

As several people mentioned already, Apple's Intro to Memory Management is by far the best place to start.

One useful link I haven't seen mentioned yet is Practical Memory Management. You'll find it in the middle of Apple's docs if you read through them, but it's worth direct linking. It's a brilliant executive summary of the memory management rules with examples and common mistakes (basically what other answers here are trying to explain, but not as well).

Papillote answered 6/2, 2010 at 3:1 Comment(0)
S
0

Objective-C Memory management

reference-counting mechanism is implementation of object ownership technic for Memory management

MRC, ARC, GC

Manual Reference Counting(MRC) or Manual Retain-Release(MRR) as a developer you are responsible for counting references on objects manually

Automatic Reference Counting(ARC) was introduced in iOS v5.0 and OS X Mountain Lion with xCode v4.2

Garbage Collection(GC) was available for Mac OS and was deprecated in OS X Mountain Lion. Must Move to ARC

Reference count in MRC and ARC

//MRC
NSLog(@"Retain Count: %d", [variable retainCount]);

//ARC
NSLog(@"Retain Count: %ld", CFGetRetainCount((__bridge CFTypeRef) variable));

Every object in heap has an integer value which indicates how many references are pointed out on it. When it equals to 0 object is deallocated by system

Lifecycle:

  • Allocating object
  • Working with Reference count
  • Deallocating object. deinit is called when retainCount == 0

MRC

A *a1 = [[A alloc] init]; //A has retainCount = 1
    
A *a2 = a1;
[a2 retain]; //A has retainCount = 2

// a1, a2 -> reference on the same object in heap with retainCount = 2

Correct way to release an object:

  1. release. If use only this technic - dangling pointer. Because it still can point on the object in heap and it is possible to send a message. For example A makes +1, B just make a reference(without +1), A makes release -1, counter == 0, object is deallocated but B has a reference to heap
  2. = nil. If use only this technic - memory leak. deinit will not be called. For example when counter == 1 and you make = nil. In any case when you forget release unused object where counter != 0 it causes memory leak
A *a = [[A alloc] init]; //++retainCount = 1
[a release]; //--retainCount = 0
a = nil; //guarantees that even somebody else has a reference to the object, and we try to send some message thought variable `a` this message will be just skipped

Working with Reference count(Object owner rules):

  • (0 -> 1) alloc, new, copy, mutableCopy
  • (+1) retain You are able to own an object as many times as you need(you can call retain several times)
  • (-1) release If you an owner you must release it. If you release more than retainCount it will be 0
  • (-1) autorelease Adds an object, which should be released, to autorelease pool. This pool will be processed at the end of RunLoop iteration cycle(it means when all tasks will be finished on the stack)[About] and after that release will be applied for all objects in the pool
  • (-1) @autoreleasepool Forces process an autorelease pool(release objects) at the end of block. It is used when you deal with autorelease in a loop and want to clear resources ASAP. If you don't do it your memory footprint will be constantly increasing

For example autorelease is helpful in method calls when you allocate a new object there and return it

- (B *)foo {
    B *b1 = [[B alloc] init]; //retainCount = 1

    //correct way 
    [b1 autorelease];

    //wrong way is to return b1 without putting it into autorelease beforehand
    return b1; 
}

- (void)testFoo {
    B *b2 = [a foo];
    //if autorelease was not used in foo
    [b2 retain]; //retainCount = 2
    //some logic
    [b2 release]; //retainCount = 1
    
    //Memory Leak
}

@autoreleasepool example:

- (void)testFoo {
    @autoreleasepool {
        for(i=0; i<100; i++) {
            B *b2 = [a foo];
            //process b2
        }
    }
}

ARC

One of biggest advantage of ARC is that it automatically insert retain, release, autorelease under the hood in Compile Time and as developer you should not take care of it anymore

Enable/Disable ARC

//enable
-fobjc-arc
//disable
-fno-objc-arc

Variants from more to less priority

//1. local file - most priority
Build Phases -> Compile Sources -> Compiler Flags(Select files -> Enter) 

//2. global
Build Settings -> Other C Flags(OTHER_CFLAGS)

//3. global
Build Settings -> Objective-C Automatic Reference Counting(CLANG_ENABLE_OBJC_ARC)

Check if ARC is enabled/disabled

Preprocessor __has_feature function is used

__has_feature(objc_arc)

Compile time

// error if ARC is Off. Force to enable ARC
#if  ! __has_feature(objc_arc)
    #error Please enable ARC for this file
#endif

//or

// error if ARC is On. Force to disable ARC
#if  __has_feature(objc_arc)
    #error Please disable ARC for this file
#endif

Runtime

#if __has_feature(objc_arc)
    // ARC is On
    NSLog(@"ARC on");
#else
    // ARC is Off
    NSLog(@"ARC off");
#endif

Reverse engineering(for Objective-C)

//ARC is enabled
otool -I -v <binary_path> | grep "<mrc_message>"
//e.g.
otool -I -v "/Users/alex/ARC_experiments.app/ARC_experiments"  | grep "_objc_release"

//result
0x00000001000080e0   748 _objc_release

//<mrc_message>
_objc_retain
_objc_release
_objc_autoreleaseReturnValue
_objc_retainAutoreleaseReturnValue
_objc_retainAutoreleasedReturnValue
_objc_storeStrong

Tool to Migrate Objective-C MRC to ARC

ARC generates errors where you should manually remove retain, release, autorelease and others issues

Edit -> Convert -> To Objective-C ARC...

New Xcode with MRC

If you enable MRC you get next errors(warnings)(but the build will be successful)

//release/retain/autorelease/retainCount
'release' is unavailable: not available in automatic reference counting mode
ARC forbids explicit message send of 'release'
Sound answered 30/11, 2023 at 21:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.