My question is how the current versions of Foundation (or of the Objective-C runtime library, since this seems to be there) implement retain count for NSObject derived objects? As I could see at NSObject.mm, there is no ivar called retain count in the NSObject's interface body. Instead, there seems to be a kind of table or map which contains references counters for each object. But if retain count is really done with a map, aren't retain and release operations too expensive with this kind of implementation (since, in this case, it's necessary to lock and unlock mutexes, lookup the map to find the right object, besides the fact that, in a multithreaded environment, only one object can be retained/released at a time)?
I didn't find anything related to setting the retain counter to 1 when allocating a new object, neither in _objc_rootAllocWithZone at NSObject.mm (which seems to be the function that is called by [NSObject alloc]) nor in _class_createInstanceFromZone at objc-runtime-new.mm (that gets called later by _objc_rootAllocWithZone).
The retain count for NSObject is indeed kept in a global map. IIRC it actually uses a set of maps that are partitioned, presumably based on the address of the object, to reduce lock contention, but the actual implementation details are just that, implementation details.
In any case, you can't find code that sets the retain count to 1 because there isn't any. Objects with a retain count of 1 aren't put into the map. Objects only enter the retain count map when they're retained past the initial 1. This is an optimization that speeds up the common case of objects that never have their retain count rise past 1.
Related
I understand that NSObject does not have a retainCount instance variable - in fact it has no instance variable relating to its lifetime or reference counted environment. My question is therefore, how does NSObject (or any object for that matter) keep track of its own reference count without an ivar?
More generally, what other systems are there for creating a reference counted environment without the use of an explicit reference-counting instance variable?
First, you should never count on a specific implementation because it could change, and it is really irrelevant for most purposes.
Anyway, the current implementation is via an associated reference count, basically via a hash table, hashing the object pointer to a reference count.
I'm trying to learn/understand what happens and why when working with or creating various objects. (Hopefully to LEARN from the docs.)
I'm reading "Programming in Objective-C 2.0" (2nd edition, by Steven Kochan). On page 408, in the first paragraph is a discussion of retain counts:
Note that its reference count then goes to 2. The addObject: method does this automatically; if you check your documentation for the addObject: method, you will see this fact described there.
So I read the addObject: docs:
Inserts a given object at the end of the array.
There, the description is missing, while other items, like arrayByAddingObject:, state it:
Returns a new array that is a copy of the receiving array with a given object added to the end.
Where in the reference does it indicate that addObject: increases the retain count? Given the presence of ARC, I should still understand what these methods are doing to avoid bugs and issues. What does ARC bring to this? (Going to read that again...)
Great question, I'm glad to see someone actually reading the docs and trying to understand them!
Since you are looking for how to research answers using Apple's documentation more so than the actual answer itself, here is how I found the answer:
First I look at the class reference for addObject: which is a method of NSMutableArray and there is no mention of memory management.
Then I look at the Overview section at the top... Hmmm, still no luck.
Since the behavior might be inherited from a parent class, I look at the Inherits from section at the top of the class reference and see that NSArray is the most immediate parent. Let's check there:
Under the Overview There is one small section about retain's:
Special Considerations
In most cases your custom NSArray class should conform to Cocoa’s
object-ownership conventions. Thus you must send retain to each object
that you add to your collection and release to each object that you
remove from the collection. Of course, if the reason for subclassing
NSArray is to implement object-retention behavior different from the
norm (for example, a non-retaining array), then you can ignore this
requirement.
Okay, I'm still not happy... Where next? The parent class of NSArray is NSObject and I know that it won't be covered there in this case (from experience) so I won't bother checking that. (If the parent was another class or something that might be covered by NSObject, I would keep moving up the tree until I found something.)
The Companion Guides usually contains a lot of good information for these types of classes. Let's try the first one, Collections Programming Topics.
The first section (after Overview) is Accessing Indexes and Easily Enumerating Elements: Arrays. Sounds promising! Click on Relevant Chapters: “Arrays: Ordered Collections”
There it is under Array Fundamentals along with a link to even more information:
And when you add an object to an NSMutableArray object, the object
isn’t copied, (unless you pass YES as the argument to
initWithArray:copyItems:). Rather, an object is added directly to an
array. In a managed memory environment, an object receives a retain
message when it’s added; in a garbage collected environment, it is
strongly referenced. When an array is deallocated in a managed memory
environment, each element is sent a release message. For more
information on copying and memory management, see “Copying
Collections.”
The book must be referring to out of date documentation because you are correct it doesn't mention anything about the retain count. It does in fact retain the object though. The way you need to think of it is not in terms of retain counts (which are useless) but rather ownership. Especially so when using ARC.
When you add an object to an NSMutableArray, it is taking ownership of that object (in ARC terminology it has a strong reference to it).
"What does ARC bring to this?"
ARC does nothing different. All ARC does (besides some optimization) is add the same release, retain, and autorelease statements that you would add yourself without using ARC. All you need to care about is that once you add an object to the array, it will live at least as long as the array.
And the arrayByAddingObject: method creates a new NSArray (or NSMutableArray) containing the object you're passing, and keeps a strong reference to the passed object. The actual array object that it creates has no references yet unless you assign it to either an ivar, property, or local variable. What you assign it to determines it's lifespan.
Basically even without ARC, it's best to think of object life-cycles in terms of ownership, ARC just formalizes that. So because of that, when using the frameworks, it doesn't matter when retains happen or don't happen, you are only responsible for your objects until you pass ownership to another object and you can trust that the framework will keep the object alive as long as it needs it.
Now of course you have to intuit what constitutes ownership. For instance delegate properties are often assign, or in ARC unsafe_unretained or weak, to prevent circular retains cycles (where two objects each retain each other), though are sometimes retained/strong so you need to look into those on a case by case basis.
And also in cases like key value observing and NSNotification observing the object you are observing does not retain the observer.
But those are really exceptions to the rule. Generally you can assume a strong reference.
Regarding this sentence above: "The actual array object that it creates has no references yet unless you assign it to either an ivar, property, or local variable. What you assign it to determines it's lifespan." I'll try to explain:
When you run this piece of code: [someArray arrayByAddingObject:someObject]; you've instantiated a new NSArray or NSMutableArray object (depending on which object type someArray is) but you haven't actually assigned it to any reference. That means that if you're using ARC, it may be immediately released afterwards, or if not using ARC, it will be released when it's autoreleasepool is drained (probably on the next iteration of that thread's runloop).
Now if instead you did this: NSArray *someOtherArray = [someArray arrayByAddingObject:someObject]; you now have a reference to the newly created array, called someOtherArray. In this case, this is a local variable who's scope is only within whichever set of { } it resides (so it could be inside an if statement, a loop, or a method. Now if you do nothing else with it, it will die sometime after it's scope ends (it isn't guaranteed to die right away, but that isn't important, you just can't assume it lives longer).
Now if in your class you have an iVar (instance variable) declared in the header like NSArray *someOtherArray; (which is strong by default in ARC) and you run someOtherArray = [someArray arrayByAddingObject:someObject]; somewhere in your class, the object will live until you either remove the reference (someOtherArray = nil), you overwrite the reference (someOtherArray = someThirdArray), or the class is deallocated. If you were not using ARC, you would have to make sure to retain that to achieve the same effect (someOtherArray = [[someArray arrayByAddingObject:someObject] retain]; which is essentially what ARC is doing behind the scenes).
Or you may have a property declared instead like #property (nonatomic, strong) NSArray *someOtherArray in which self.someOtherArray = [someArray arrayByAddingObject:someObject]; would achieve the same effect but would use the proprety accessor (setSomeOtherArray:) or you could still use someOtherArray = [someArray arrayByAddingObject:someObject]; to set the iVar directly (assuming you #synthesized it).
Or assuming non-ARC, you might have declared the property like #property (nonatomic, retain) NSArray *someOtherArray in which self.someOtherArray = [someArray arrayByAddingObject:someObject]; would behave exactly as ARC would, but when setting the iVar directly you would still need to add that retain manually.
I hope that clears things up a bit, please let me know if there's anything I glossed over or left out.
As you mentioned in your comment, the key here is intuitively knowing when an object would be considered owned by another one or not. Luckily, the Cocoa frameworks follow a pretty strict set of conventions that allow you to make safe assumptions:
When setting an NSString property of a framework object (say the text property of a UILabel for example) it is always copied (if anyone knows of a counter-example, please comment or edit). So you don't have to worry about your string once you pass it. Strings are copied to prevent a mutable string from being changed after it's passed.
When setting any other property other than delegate, it's (almost?) always retained (or strong reference in ARC)
When setting delegate properties, it's (almost?) always an assign (or weak reference) to prevent circular retain cycles. (For instance, object a has a property b that is strong referenced and b has a strong referenced delegate property. You set a as the delegate for b. Now a and b are both strongly referencing each other, and neither object will ever reach a retain count of 0 and will never reach it's dealloc method to dealloc the other object. NSURLConnection is a counter-example that does strongly reference it's delegate, because it's delegate is set via a method -- see that convention below -- and it's convention to nil out or release an NSURLConnection after it completes rather than in dealloc, which will remove the circular retain)
When adding to an array or dictionary, it's always retained (or strong reference).
When calling a method and passing block(s), they are always copied to move them from the stack (where they are initially created for performance purposes) into the heap.
Methods that take in object parameters and don't return a result immediately are (always? I can't think of any that don't) either copying or retaining (strong referencing) the parameters that you pass to ensure that the method can do what it needs to with them. For instance, NSURLConnection even retains it's delegate because it's passed in via a method, whereas when setting the delegate property of other objects will not retain, as that is the convention.
It's suggested that you follow these same conventions in your own classes as well for consistency.
Also, don't forget that the headers of all classes are available to you, so you can easily see whether a property is retain or assign (or strong or weak). You can't check what methods do with their parameters, but there's no need because of the convention that parameters are owned by the receiver.
In general, you should look in the "most global" spot for information about anything in the Cocoa APIs. Since memory management is pervasive across the system APIs and the APIs are consistent in their implementation of the Cocoa memory management policy, you simply need to read and understand the Cocoa memory management guide.
Once understood, you can safely assume that all system APIs implement to that memory management policy unless explicitly documented otherwise.
Thus, for NSMutableArray's addObject: method, it would have to retain the object added to the array or else it would be in violation of that standard policy.
You'll see this throughout the documentation. This prevents every method's documentation from being a page or more long and it makes it obvious when the rare method or class implements something that is, for whatever reason (sometimes not so good), an exception to the rule.
In the "Basic Memory Management Rules" section of the memory management guide:
You can take ownership of an object using retain.
A received object is normally guaranteed to remain valid within the
method it was received in, and that method may also safely return the
object to its invoker. You use retain in two situations: (1) In the
implementation of an accessor method or an init method, to take
ownership of an object you want to store as a property value; and (2)
To prevent an object from being invalidated as a side-effect of some
other operation (as explained in “Avoid Causing Deallocation of
Objects You’re Using”).
(2) is the key; an NS{Mutable}Array must retain any added object(s) exactly because it needs to prevent the added object(s) from being invalidated due to some side-effect. To not do so would be divergent from the above rule and, thus, would be explicitly documented.
Apple's Advanced Memory Management doc is pretty well written and precise, but I wonder for its diagram it has:
(mine is the current revision 2011-09-28)
In the diagram, after the alloc and init, the reference count (or the retain count) is 1, which is the basic memory management rule. After the retain, the reference count is now 2. And then Class C sends a copy message to the object, and after that, the object still has a reference count of 2. The new object has a reference count of 1.
So far this conforms to the rules, but next, on the top of the diagram, Class A sends a release message to the object, and the reference count should be a 1? The diagram has a 2 instead. Then Class B also sends a release message to the object, and the reference count now should be 0. The diagram shows a 1 instead. Is this correct, or maybe there is a different way to read the diagram, or maybe some concept is not correct above?
I think the diagram is a little misleading in two ways. One is that for the alloc and retain steps, the associated counts are what happens as a result of the operation. At the release steps, however, the counts appear to be the state before the operation. That's all that makes sense to me because there is no "Destroyed" method to cause a 1-to-0 transition.
The other misleading part is, I don't think the retain count ever truly goes to zero and I've seen people get confused by believing that it does.
Simply read as : It has a retain count of 2 before the release message, then 1.
It would have been preferable for better understanding to put retain count on the right of the object circle in the retain / alloc messages.
I have read that objects retain count and that it can be increased when we assigned a second value (or object).
Can anybody give me an idea about the basic conditions where retainCount increases or decreases (without retain , alloc and release)...
Short answer: no.
Slightly longer one:
Actions on your part that are typically expected to modify the retain count are retain, release, autorelease and calling methods whose name contains new or copy. In some instances, however, they might not do so, for good implementation reasons, but do something else instead. And there are many other things you can do, like adding objects to collections, that may modify the retain count in ways that are just not your business.
Do not use or rely on retain counts. They are an implementation detail.
Your concern is to manage your own ownership, which you do via the semantics of the above-mentioned methods. How that affects the retain count beneath the hood is something you are better off not even looking at.
Please just don't.
Being concerned about retain count is almost always a way to cause bugs, not cure them.
The retain count of an object is an implementation detail that you shouldn't worry about. You should really only concern yourself with when you gain and lose ownership of an object. Here's why:
+alloc returns an object with a retain count of 1. However, you immediately send it an init message which might give you back a completely different object. For instance
NSString* foo = [[NSString alloc] initWithString: #"foo"];
Gives you an object with a retain count of 1, right? Wrong on current implementations of Cocoa. The string returned by +alloc is immediately released and instead a reference to #"foo" is returned. The retain count of #"foo" is INT_MAX.
Retaining an object increases its retain count, right? Probably, but again sending retain to constant strings has no effect and some singletons have a nul implementation of retain too.
Similarly release usually has an effect on the retain count, but again not necessarily for the same reasons.
Sending copy should give you a new object with a retain count of 1 right? Wrong. Immutable objects can implement copy by sending themselves retain and then returning themselves. In most cases, that will add one to the retain count, but of course with string constants, nothing happens.
If you just think in terms of retaining and releasing ownership all of that stuff I have just said becomes irrelevant implementation details.
retainCount of an object changes in the following cases:
When you create an object(new or
alloc, copy or mutablecopy), it has a
retain count of 1.
When you send an object a retain
message, its retain count is
incremented by 1.
When you send an object a release
message, its retain count is
decremented by 1.
When you send a autorelease message
to an object, its retain count
will be decremented by 1(not
immediately as in case of release but
some time in the future)
You can view this post to get a detailed info on how memory management works in iPhone.
I'm working through Aaron Hillegass' book, specifically the lottery example. I had a question about the -setEntryDate: method; why do I have to retain date? The program still works without retaining it.
-(void)setEntryDate:(NSCalendarDate *)date {
[date retain];
[entryDate release];
entryDate = date;
}
But this still works fine:
-(void)setEntryDate:(NSCalendarDate *)date {
entryDate = date;
}
So why is it correct that I have to retain date and then release entryDate?
It works for now, but if you were writing a larger program there is a possibility that, at some indeterminable point in the future, the object date points to would be released by whomever called setEntryDate. If that happened, it would be invalidated throughout the rest of the program. You are retaining this object in the class because that class now owns a reference to that object and needs to indicate that. By doing this, even if whatever class called setEntryDate were to release date, your class would still maintain a valid reference to it. Also, this is not just a regular old method you are writing. This is a setter, which has the specific responsibility of setting an instance variable on the class it belongs to. If you were writing a non-setter method, you may not have to retain the parameters. What I'm trying to say is that retaining method parameters is not always necessary; it just is in this case (and pretty much with all setters that deal with non-primitive types).
This is called "reference counting" and is explained in great detail here. For now, since you are just starting to learn, don't worry about reading that yet. When you start to get into more complex scenarios with memory management, then that guide is a very valuable piece of reading.
Because you are claiming ownership of the object, and retain is how you do this. Incorrect code will sometimes happen to work correctly, but that is essentially just luck.
See the Cocoa memory management rules.
Methods such as these are called accessor methods. They, as the name implies, allow variables to be retrieved, and set - specifically, they are called "getters" and "setters".
The convention (which, you will however see in later chapters of the book is more than a convention) is to call the "getter" for a variable, for example, an NSString called foo
- (NSString*)foo;
And the "setter":
- (void)setFoo:(NSString*)newFoo;
In the example above, the method is implemented to set the a new date value. Memory management is described in Chapter 4, but in short, the way Objective-C objects work is that they have a "retain count" - this is representitive of the number of references that the object has; when allocated, objects have a retain count of 1. The objects can then be sent the retain or the release message to increase or decrease the retain count respectively. A retain message implies that the object sending the message wants to use the object, so retains it. A release message implies that the object sending the message no longer wants to use the object, therefore decreasing the retain count. When the retain count of an object reaches 0, the object is deallocated. In this way, memory leaks are avoided.
The reason that date is retained and entryDate is released, is that date is the new object that you want to "know about"; you are therefore claiming ownership of it by retaining it. The entryDate variable points to the current date object, but since you are setting it to be a new value, you no longer need to know about it; therefore you release it; this prevents a memory leak, since you will have originally retained this variable.
As I said before, once reading Chapter 4: Memory Management, the concept will become much more clear. For now, just accept it and understand the reasoning behind it when it is explained.
If you learn by videos better, Stanford University has published some videos on the iPhone development which also cover Cocoa and Objective-C to a certain extent. Check out lecture 3, it makes a good overview on memory management, with examples and discussion.