How is retain count implemented in NSObject?
Asked Answered
L

1

5

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).

Lomond answered 15/2, 2013 at 23:40 Comment(2)
-(NSUInteger)retainCount{ return rand() };Oarfish
Oh good god, I've misplaced the terminating ;... I will not sleep tonight.Oarfish
G
10

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.

Greco answered 15/2, 2013 at 23:46 Comment(6)
... with the special case of tagged pointers that don't have a retain count at all. The data itself is encoded within the pointer so you're technically passing them around by value rather than by reference (because the reference is the value and there's nothing real behind the reference).Supraorbital
True. And of course any class that overrides -retain and -release is free to have its own implementation of the retain count. There are a number of classes that do embed the retain count as an ivar, because they prefer to speed up retain and release at the expense of having a larger object.Greco
Ok. But why don't they use an ivar as a counter, which would reduce the cost of retaining/releasing? Do you know whether there is any special reason for using maps?Lomond
@LuisAntonioBotelhoO.Leite: That would increase the size of every object on the system by 4-8 bytes (depending on architecture). This could cause whole classes of objects to go up by one malloc bucket size (e.g. 16 to 32 bytes). There are also lots of objects that never rise past a retain count of 1, and that would cause these objects to waste space unnecessarily.Greco
I had already thought about the increased size of the instances of objects using ivars. But maps would eat even more memory. However, since most objects never rise past a retain count of 1, as you said, using maps is pretty understandable. Thank you very much.Lomond
I feel confident you can rest assured that retain and release are heavily optimized as well, and that you needn't worry about the performance implications. In all my years of working with Obj-C, I can't recall ever seeing retain and release making enough of a blip on a time profile to warrant giving it even a first thought.Carolinecarolingian

© 2022 - 2024 — McMap. All rights reserved.