Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

foidulus

macrumors 6502a
Original poster
Jan 15, 2007
904
1
this is just out of curiosity more than anything else, but when using ObjC without auto GC turned on, does calling release on an object with a reference counter of 1 act like the *free call in standard c or is there some behind the curtains magic going on where the memory reference gets placed on some queue to be reclaimed by another thread?
 
It depends on how the object was created in the first place.

If you created an object using the following.

NSFoo bar = [[[NSFoo alloc] init] autorelease];

The variable bar is added the NSAutoreleasePool and gets released, i.e. a call to release is generated if the retain count is 0, when the thread managing the pool cycles.

If on the other hand you allocated it like

NSFoo bar = [[NSFoo alloc] init];

Then it will only be freed when you call release, and as long as the retain count is 1 when you call release.

So in short release is like free in C, when there is only one retain count on an object, but when the retain count is > 1, then release just decrements the release count and does not free the memory by calling your dealloc method.

Memory management in Objective-C isn't very difficult, but today I would just advise people to switch on GC for Mac apps, and if you are coding iPhone, iPad, etc apps, then just read the chapters on memory management in Stephen Kochan's books.
 
Memory management in Objective-C isn't very difficult
I got the impression that the OP is familiar with the basics of memory management, but was wondering how the actual internals are implemented.

Matt Gallagher has an interesting article called "A look at how malloc works on the Mac". While that article focuses on the low-level internals of memory allocation, I believe I've read something about some of the performance kung-fu that goes on behind the scenes in terms of how deallocs are handled as well (I can't seem to find it though). That article should at least give you a sense of the types of things being done behind the scenes.
 
To follow on from GorillaPaw's comments, my understanding is that release of an object with retain count 1 causes that object's storage to be freed immediately from the program's point of view. The OS then applies its own policies with respect to when that same block of memory may next appear as a result of a fresh malloc, if it ever does. So there's no magic happening in userland but possibly some happening up in the kernel.
 
Thomas is correct. Objects are deallocated as soon as their refcount hits zero. Autorelease acts via release, so doesn't change this fundamental principal.
 
Objects are deallocated as soon as their refcount hits zero.

This is subtly and fiendishly wrong.

A dealloc'ed object's retaincount is never actually set to zero: not before dealloc is called, nor after. There is no need to set a dealloc'ed object's actual retaincount to zero, because the object's memory is about to be freed, so the values of its ivars no longer matter.

I mention this because it's not uncommon for Objective-C newbies to try to understand deallocation by looking at the value returned by -retaincount. They almost invariably interpret the value 1 as indicating "this object has one owner" or "this object is valid", or as a check that an object hasn't been over-released.

It's also common to interpret the retaincount as an exact count of the ownership claims made on the object (i.e. the count of retains). This is also wrong: the retaincount may be a very large number (or indeed any non-zero number) that is never decremented. For example, check the retaincount of any @"Literal" some time.

This is all discussed in Apple's memory management guide.
 
So the os never implements clever tricks behind-the-scenes to re-use freed memory? Like if I alloc and init an object, release it, and then create a second object of the same class, will it not see this and just re-assign the memory, returning it as a new object? The compiler might optimize this out though right?
 
So the os never implements clever tricks behind-the-scenes to re-use freed memory? Like if I alloc and init an object, release it, and then create a second object of the same class, will it not see this and just re-assign the memory, returning it as a new object? The compiler might optimize this out though right?

Think about this: what if your app is multi-threaded? You could never be certain when a given thread has a CPU, so if you free an object, you would have to implement some kind of wide-band lock to insure no other memory manipulation activity is taking place.
 
Think about this: what if your app is multi-threaded? You could never be certain when a given thread has a CPU, so if you free an object, you would have to implement some kind of wide-band lock to insure no other memory manipulation activity is taking place.

That's a good point that I hadn't considered. I guess I was just thinking about some of the points Mike Ash makes in this article discussing the keyword "volatile". He makes this point about how the compiler works:
[The C standard] states that the program must execute as if all the specified actions were executed in a virtual machine. In other words, given the code above, at the end of the day x must contain 7, y must contain 4, and z must contain 7. But how it actually gets there is entirely up for grabs. A smart compiler will just do all of the computations at compile time and generate code that dumps those final values into the variables right away, instead of doing math every time through.

I wasn't sure if it could re-used memory in certain situations (like when it's in a very tight loop). As you point out, multi-threading would be a problem.
 
This is subtly and fiendishly wrong.

A dealloc'ed object's retaincount is never actually set to zero: not before dealloc is called, nor after. There is no need to set a dealloc'ed object's actual retaincount to zero, because the object's memory is about to be freed, so the values of its ivars no longer matter.

I mention this because it's not uncommon for Objective-C newbies to try to understand deallocation by looking at the value returned by -retaincount. They almost invariably interpret the value 1 as indicating "this object has one owner" or "this object is valid", or as a check that an object hasn't been over-released.

It's also common to interpret the retaincount as an exact count of the ownership claims made on the object (i.e. the count of retains). This is also wrong: the retaincount may be a very large number (or indeed any non-zero number) that is never decremented. For example, check the retaincount of any @"Literal" some time.

This is all discussed in Apple's memory management guide.

Sigh, yes, I meant conceptually. I keep forgetting people try to use -retaincount for anything. I wish it was deprecated.

However, you're slightly wrong as well. I implemented a custom retain/release for performance recently, and it did* indeed hit zero before deallocation ;)

*I say did because I now store the retain count minus one, so that the initial zero initialization is "correct" for starting with refcount one.
 
Typically, when you release an object, you should think of it as gone, though there are exceptions to this. If you create an array, you may want to destroy the whole array in a single shot: if the objects belong to the array, you should release them as soon as you add them to the array so that they will go away with it (arrays retain objects added to them and release objects removed from them, including when the array is released). In such a case, if you take an object from a mutable array, you should always retain it before you remove it so that it does not deallocate as soon as you pull it out (unless you are absolutely certain that something else owns the object). I guess it might have been nice if Apple had devised some sort of ownership trail, like a series of links or some kind of array, so that objects' owners could be easily identified and the objects deallocated only when all their owners were gone.
 
I wasn't sure if it could re-used memory in certain situations (like when it's in a very tight loop). As you point out, multi-threading would be a problem.

Not just multi-threading. The call to the free() itself could potentially allow reuse of the memory block inside free() for some memory management purpose (new superblock, flag bits, and/or etc.)

If you want to take advantage of some predictable memory reuse patterns, it might be best to write your own caching allocation layer, on top of what the frameworks/OS provides.
 
It's good to know that nobody on this forum still reads. If you read my answer, I answer the original OP's question at the end.

The answer is, it depends, if you are using GC and whether the retain count is > 0.
 
It's good to know that nobody on this forum still reads. If you read my answer, I answer the original OP's question at the end.

Your answer didn't address anything about "behind the curtains magic" which is what the OP asked. You simply regurgitated basic memory management, that can easily be found in the docs.
 
To be honest, no one has actually answered the question.

How is memory allocated with the alloc call?

E.g.

NSFoo *foo = [[NSFoo alloc] init];

What exactly does alloc do? Answer? Depends on what you are allocating.

OS X Internals explains exactly how the memory retain/release cycle works in OS X. It's definitely more complicated than it needs to be, but that is a throwback to the NextStep days. Basically it would call malloc, localalloc, calloc, realloc, depending on how much memory was required, and then would call the mirror free function to correctly deallocate the memory. Luckily, when you today use Objective-C's alloc and init functions, you can blissfully allocate memory and not worry about how the magic works.
 
To be honest, no one has actually answered the question.

How is memory allocated with the alloc call?

E.g.

NSFoo *foo = [[NSFoo alloc] init];

What exactly does alloc do?

[NSFoo alloc] allocates the right amount of memory for an instance of class NSFoo, sets all bits to zero, fills in everything that is needed so that it's not raw memory but an object of class NSFoo, and sets the retain count to 1. (Difference to C++: C++ doesn't initialise the memory to zero, and if you use inheritance then the object starts as an instance of the base class, executes the constructor while being an instance of the base class, then is turned into an instance of the derived class and executes the constructor of the derived class. In Objective-C, an object is already a member of the derived class while the init method executes).

[myobject release] first checks whether the object is a special kind of object that never gets released, like @"mystring" is an object that you can release as often as you like, it never goes away. If it is not one of those special objects then "release" checks the retain count. If the retain count is greater than 1 then it subtracts 1 from the retain count. If the retain count is equal to 1, then it calls the [myobject dealloc] method, and then deallocates the memory.

Allocating and deallocating are done with a variant of malloc and free. The "dealloc" method should better not modify the object's retain count; it might be legal to do retain/release in pairs in the dealloc method (don't do it unless you find official documentation that says you can); an unmatched "retain" inside the dealloc method is a bad idea, and an unmatched "release" is a very, very, very bad idea.
 
The documentation clearly states that when -init begins, all the ivars are in a zero/nil-state, so the memory block associated with an object allocation is either created with calloc() or explicitly cleared by Objective-C. The -alloc method simply finds the first block big enough to hold the object frame (this is probably an effect of the calloc() routine), so pointers get reused quite a lot: if you ever accessed a dangling pointer (due to a coding flaw), you might have discovered the object had actually become something else. There does not appear to be any optimization magic going on behind the scenes, apart from some constants used by some of the immutable classes like NSNumber.
 
Retain Counts

I find that teaching retain counts does help new Objective-C programmers grasp the concepts of memory management. Certainly, the caveats about special retain counts (for literals and NSNumber objects) and to never rely on the retain count programmatically are stressed. However, when I interactively show an object and ask my students what they think the retain count will be for an object after it is sent an autorelease message, or how the retain and release messages work together by displaying the retain count before and after the messages are sent, helps them to understand the concepts. I also use this to illustrate what happens to an object after it is added and removed to and from a collection, or when the autorelease pool is drained. Finally, it is important for them to understand when dealloc is called, and the retain count is the trigger for that action.

Bottom line is that I think the retain count is a useful teaching and learning tool. It probably shouldn't be used in programs otherwise.

[This has nothing to do with the original question, of course :). However, there were some implications here about the horrors of newbies leaning about memory management via retain counts.]

Cheers,

Steve Kochan
 
I find that teaching retain counts does help new Objective-C programmers grasp the concepts of memory management. Certainly, the caveats about special retain counts (for literals and NSNumber objects) and to never rely on the retain count programmatically are stressed. However, when I interactively show an object and ask my students what they think the retain count will be for an object after it is sent an autorelease message, or how the retain and release messages work together by displaying the retain count before and after the messages are sent, helps them to understand the concepts. I also use this to illustrate what happens to an object after it is added and removed to and from a collection, or when the autorelease pool is drained. Finally, it is important for them to understand when dealloc is called, and the retain count is the trigger for that action.

Probably worth noting that copying objects might not do what the naïve programmer expects: When you ask for a copy of an immutable object, you likely get the original object with the retain count increased.
 
Probably worth noting that copying objects might not do what the naïve programmer expects: When you ask for a copy of an immutable object, you likely get the original object with the retain count increased.

Yes, that is discussed in an example implementing a copyWithZone: method for immutable objects.

Cheers,

Steve Kochan
 
Probably worth noting that copying objects might not do what the naïve programmer expects: When you ask for a copy of an immutable object, you likely get the original object with the retain count increased.
I have a hard time understanding how that could be a problem.
 
[NSFoo alloc] allocates the right amount of memory for an instance of class NSFoo, sets all bits to zero, fills in everything that is needed so that it's not raw memory but an object of class NSFoo, and sets the retain count to 1. (Difference to C++: C++ doesn't initialise the memory to zero, and if you use inheritance then the object starts as an instance of the base class, executes the constructor while being an instance of the base class, then is turned into an instance of the derived class and executes the constructor of the derived class. In Objective-C, an object is already a member of the derived class while the init method executes).

[myobject release] first checks whether the object is a special kind of object that never gets released, like @"mystring" is an object that you can release as often as you like, it never goes away. If it is not one of those special objects then "release" checks the retain count. If the retain count is greater than 1 then it subtracts 1 from the retain count. If the retain count is equal to 1, then it calls the [myobject dealloc] method, and then deallocates the memory.

Allocating and deallocating are done with a variant of malloc and free. The "dealloc" method should better not modify the object's retain count; it might be legal to do retain/release in pairs in the dealloc method (don't do it unless you find official documentation that says you can); an unmatched "retain" inside the dealloc method is a bad idea, and an unmatched "release" is a very, very, very bad idea.

Thank you, that was the answer I was looking for, so basically all of the dealloc fun occurs on the thread that the last release was called on(vs. gc mode where all that takes place on a separate thread). Am I correct in this assessment?
 
I have a hard time understanding how that could be a problem.

For example if you compare whether two object pointers are the same and expect the original and the copy to be different.


Thank you, that was the answer I was looking for, so basically all of the dealloc fun occurs on the thread that the last release was called on(vs. gc mode where all that takes place on a separate thread). Am I correct in this assessment?

That is correct.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.