How to understand ARC in objc - ios

The code is as follows, Why the output of the following reference count is 1,2,3,4 respectively?
NSDictionary __weak *weak_dict;
#autoreleasepool {
//生成并持有对象 alloc,new,copy,mutableCopy (注意以这些词开头的命名)
NSDictionary *dict = [ [NSDictionary alloc] initWithObjects:#[#1, #2] forKeys:#[#"a", #"b"] ];
NSDictionary *dict1 = #{ #"1":#"c" };
weak_dict = dict;
NSLog(#"dict retain count : %ld", _objc_rootRetainCount(dict)); //1
NSLog(#"dict retain count : %ld", _objc_rootRetainCount(weak_dict)); //2
NSLog(#"%# CFGetRetainCount %ld", weak_dict, CFGetRetainCount((__bridge CFTypeRef)weak_dict) ); //3
NSLog(#"dict retain count : %ld", _objc_rootRetainCount(weak_dict)); //2
}

First, it's critical that your realize that retainCount is useless. Using an internal function like _objc_rootRetainCount doesn't change that. The values output are completely compiler-dependent and can change at any time due to optimization or framework changes. While there's a certain benefit to digging into the implementation details, chasing the actual value of retain counts more often causes developers to go astray rather than illuminate anything. (But do read the comments between Alexander and me below; this is all true, but also misleading. There are reasons to look at retain counts.)
That said, it is not magic, and is totally knowable. There just isn't any specific rule. You have to look at exactly what the optimizer has done. All discussion here is for Xcode 13.0 Beta 5 in Debug, built for Mac ARM. Other versions or configurations could return different values because some of the retains here could be removed if the optimizer were smarter (and it might be in the future). You can explore this all by looking at the Assembly output, or by using the Hopper decompiler (which is what I did).
NSLog(#"dict retain count : %ld", _objc_rootRetainCount(dict)); //1
No surprises here I hope. _objc_rootRetainCount prints the retain count and that's 1 because you called alloc to create it. There's no promise that this be 1, though. It could be any positive value. The only thing you're actually promised that you are responsible for sending exactly one release, and if you do that, everything will balance.
NSLog(#"dict retain count : %ld", _objc_rootRetainCount(weak_dict)); //2
Since weak_dict is weak, it needs to first create a strong reference before passing to _objc_rootRetainCount (generally the caller for a function is responsible for ensuring that the arguments it passes continue to exist for the lifetime of the call). This effectively behaves like:
id strong_weak_dict = [weak_dict retain];
NSLog(#"dict retain count : %ld", _objc_rootRetainCount(strong_weak_dict));
[strong_weak_dict release];
A smarter future optimizer might be able to figure out that the existing dict reference is equivalent and avoid this extra retain/release. There is no promise that it happens. The only requirement is that the caller must ensure the argument's lifetime extends beyond the call to _objc_rootRetainCount. It can do that any way it likes.
NSLog(#"%# CFGetRetainCount %ld", weak_dict, CFGetRetainCount((__bridge CFTypeRef)weak_dict) ); //3
Same thing here, there are just two copies of weak_dict passed to two different functions (NSLog and CFGetRetainCount). The compiler inserts two retains and two releases:
id strong_weak_dict = [weak_dict retain];
[strong_weak_dict retain];
NSLog(#"%# CFGetRetainCount %ld", strong_weak_dict, CFGetRetainCount((__bridge CFTypeRef) strong_weak_dict) );
[strong_weak_dict release];
[strong_weak_dict release];
It could be smarter and get rid of one of those (or it could even get rid of both if it realized that dict is doing the job already).
NSLog(#"dict retain count : %ld", _objc_rootRetainCount(weak_dict)); //2
And as above, passing weak_dict wraps a retain/release, so you get 2.
One slightly odd thing in the output from 13.0 Beta 5 is that dict1 has an extra retain with no corresponding release. I'm not sure why that happens. (This is fine since retain and release are no-ops for constants, I just don't understand why the call to retain is inserted.)
There are several details I glossed over here to make the behavior a bit simpler. The compiler doesn't call retain, it calls objc_loadWeakRetained. The specific way it handles the "double retain" includes an extra temporary variable. And there's a call to objc_storeWeak that I didn't discuss, but also doesn't directly impact your question.
There is a performance lesson in here, which is that unnecessary use of weak can insert a lot of extra retains and releases, which are not free. Generally you should convert weak pointers to strong pointers one time in a function and then use the strong pointer. This also ensures that the object survives the entire function, which is better for reasoning about it. Weak pointers can become nil between one statement and the next. So this is a good question and worth exploring; you just shouldn't rely on any of these details.
Here's the full pseudo-C decompiled output:
int _main(int arg0, int arg1) {
var_48 = objc_autoreleasePoolPush();
var_20 = [objc_alloc() initWithObjects:r2 forKeys:0x1000080f8];
[0x100008110 retain];
objc_storeWeak(&saved_fp - 0x18, var_20);
stack[-192] = _objc_rootRetainCount();
NSLog(#"dict retain count : %ld", #selector(initWithObjects:forKeys:));
var_68 = objc_loadWeakRetained(&saved_fp - 0x18);
stack[-192] = _objc_rootRetainCount();
NSLog(#"dict retain count : %ld", #selector(initWithObjects:forKeys:));
[var_68 release];
var_80 = objc_loadWeakRetained(&saved_fp - 0x18);
r0 = objc_loadWeakRetained(&saved_fp - 0x18);
var_78 = r0;
r0 = CFGetRetainCount(r0);
stack[-192] = var_80;
*(&stack[-192] + 0x8) = r0;
NSLog(#"%# CFGetRetainCount %ld", #selector(initWithObjects:forKeys:), 0x1000080e0);
[var_78 release];
[var_80 release];
var_98 = objc_loadWeakRetained(&saved_fp - 0x18);
stack[-192] = _objc_rootRetainCount();
NSLog(#"dict retain count : %ld", #selector(initWithObjects:forKeys:));
[var_98 release];
objc_storeStrong(&saved_fp - 0x38, 0x0);
objc_storeStrong(&saved_fp - 0x20, 0x0);
objc_autoreleasePoolPop(var_48);
objc_destroyWeak(&saved_fp - 0x18);
return 0x0;
}

Related

Passing an object around increases retain count

iOS, transitioning to ARC. I've observed a curious behavior regarding CF/NS bridging. In the following scenario:
CFStringRef cfs = ComesFromSomewhere();
NSString *ns = (__bridge NSString*)cfs;
the retain count of the string object is 2 at the end. However, in the following:
NSString *ToNS(CFStringRef cfs)
{
return (__bridge NSString*)cfs;
}
CFStringRef cfs = ComesFromSomewhere();
NSString *ns = ToNS(cfs);
the retain count is 3 at the end. What's going on, please? Who holds the extra reference? Is the object being added to the autorelease pool by the mere act of passing it around?
Preemptive response to "don't worry, ARC just works": I'm mixing Core Foundation with Cocoa here, no way around it. This is leak prone. Without the ability to account for the retain counts explicitly, I'm flying blind.
EDIT: it's an artifact of the debug build. In the release build, the retain count under the latter scenario is still 2.
There's a tangible difference between a fragment that leaves large autoreleased objects around and one that doesn't; you don't want the former in a big loop without a pool in the loop body. Helps to know it's an artifact of zero optimization, but still, not cool.
CFStringRef cfs = ComesFromSomewhere();
// retainCount -> 1
NSString *ns = ToNS(cfs);
// ToNS(cfs)
//
// ToNS is not object creating method,
// thus the returned object was automatically autoreleased
// retainCount += 1
// NSString *ns
//
// It's __strong variable, ns variable has an ownership of the object
// retainCount += 1
// retainCount -> 3
The definition of object creating method is a method whose name begins with “alloc”, “new”, “copy”, or “mutableCopy”, of a Objective-C class. See Basic Memory Management Rules - You own any object you create.
In the release build, the retain count under the latter scenario is still 2.
Also compiler can omit to send autorelease message to object if it's eligible.
EDITED
You can use C++ reference to avoid autorelease.
void ToNS(CFStringRef cfs, NSString __strong *& ns)
{
ns = (__bridge NSString*)cfs;
}
NSString *nsstr;
ToNS(cfstr, nsstr);
// retainCount -> 2
EDITTED
NS_RETURNS_RETAINED NSString *ToNS(CFStringRef cfs)
{
return (__bridge NSString*)cfs;
}
NS_RETURNS_RETAINED makes the framework treat the function as an object creating one (which it really is). Cocoa has a name convention that lets you designate a method as an object creator, but the convention only applies to Objective C class methods, not to C style functions and not to C++ class member functions.

Right way to use __attribute__((NSObject)) with ARC?

I just use CFNumber as a example,so it can be any type don't have a Fundation toll-free part!
I just write some test code like this:
typedef __attribute__((NSObject)) CFNumberRef MYNumberRef;
int main(int argc, const char * argv[])
{
#autoreleasepool {
MYNumberRef ptr = NULL;
double myDouble = 10.1;
ptr = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &myDouble);
CFIndex count = CFGetRetainCount(ptr);
}
return 0;
}
It is very strange that the count is 2. But if I use CFNumberRef, the count is 1. It seems the arc don't take the CFType name convention into account, it just retains the return value.
So if I use the __attribute__((NSObject)) to declare CFType property. This post said you shouldn't have to explicitly nil them out in dealloc. But if I use like this:
#property (strong, nonatomic, readwrite) __attribute__((NSObject)) CFNumberRef number;
Then:
self.number = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &myDouble);
There is no memory leak if I don't release it in the dealloc method? Maybe I should use it like this:
CFNumbeRef ref = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &myDouble);
self.number = ref;
CFRelease(ref);
Does Apple say something about this?
The type attribute __attribute__((NSObject)) does not mean that ARC will manage the memory for that type exactly the same way as if that type was an Objective-C object.
The attribute only means that in situations where the compiler automatically generates code for handling that type and the generated code is different depending on whether that type is a normal pointer or an Objective-C object pointer, the compiler should generate the code version for Objective-C objects.
This basically only influences two situations (as of today):
When using these pointers within blocks (^{ }), as objects get automatically retained when being captured by blocks and released again when the block is destroyed.
When assigning values to properties (#property) because when assigning objects to strong (= retain) properties, they get retained and the old property value gets released.
Both situations have nothing to do with ARC. Even in a non-ARC environment the compiler will generated code that works this way.
The reason why your code didn't work as expected is simply because it is wrong.
self.number = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &myDouble);
CFNumberCreate() creates a new object with a retain count of (at least) 1 but this object is not managed by ARC, applying the attribute __attribute__((NSObject)) to the type won't change that. So somewhere this create must be balanced with a release.
When you assign the value to self.number, it gets retained again, this time because of the __attribute__((NSObject)) attribute, so now the retain count is (at least) 2. When your object is deallocated, self.number is set to nil, which balances the second retain but there is nothing that would balance the initial create and thus the object will leak.
Consider this code:
#property(nonatomic) NSNumber * number;
self.number = [[NSNumber alloc] initWithInt:10];
Without ARC this code would leak as well, because again, alloc/init produces an object with a retain count of (at least) 1, when you assign it to self.number, the retain count becomes (at least) 2. Even if you do self.number = nil; in dealloc, this only balances the retain of the property, but where would the alloc/init get balanced?
Only with ARC this code won't leak as with ARC, the code the compiler generates looks like this:
NSNumber * tmp = [[NSNumber alloc] initWithInt:10];
self.number = tmp;
[tmp release];
And last line balances the first line, the object is still kept alive by self.number, unless you set self.number = nil; in dealloc which ARC will also do for you so with ARC, there is nothing else you have to do because ARC takes care of any object created with alloc.
That said, keep in mind that
[NSNumber numberWithInt:10]
which is the same as #(10) in modern Obj-C, is in fact equal to
[[[NSNumber alloc] initWithInt:10] autorelease]
But even when you use ARC, it won't care for references created by CFNumberCreate(). To fix your code, it should be:
CFNumberRef num = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &myDouble);
self.number = num;
CFRelease(num);
Now the last line balances the first line, but the property keeps the CFNumberRef alive and this is automatically balanced by dealloc when using ARC or when not using ARC, must be balanced with a self.number = nil; in dealloc.
If you use ARC, you can also do this instead:
self.number = (__bridge CFNumberRef)(__bridge_transfer id)
CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &myDouble);
(__bridge_transfer id) tells the compiler that the value returned by CFNumberCreate() is an Objective-C object that has a retain which needs to be balanced and ARC should take ownership of this object and take care of the balance. (__bridge CFNumberRef) then casts the type back to CFNumberRef but without transferring ownership away from ARC. Without ARC the code above leaks again, yet the compiler will warn you without ARC that bridge casts do nothing.
By the way, to transfer ownership away from ARC, the cast would be (__bridge_retained CFNumberRef), in which case ARC will once retain the object for you and then give you a reference that you must release yourself to balance that retain (now you own the object reference and ARC won't manage it anymore for you).
Do not do this.
Apple does have something to say about it in the Clang documentation:
The use of __attribute__((NSObject)) typedefs is not recommended. If it’s absolutely necessary to use this attribute, be very explicit about using the typedef, and do not assume that it will be preserved by language features like __typeof and C++ template argument substitution.
CFGetRetainCount is meaningless. Worse than meaningless because you think it might mean something.

Nested NSArray filtering

I have the need to obtain the maximum value of a property of a collection of custom objects of the same class. The objects are stored in a NSArray, and the property happens to be another NSArray of numbers.
Let me explain in detail:
NSArray *samples; // of CMData, 4000 elements
CMData is a class that models a sample, for a specific moment in time, of a set of different channels that can have different values.
#interface CMData : NSObject
#property (nonatomic) NSUInteger timeStamp;
#property (nonatomic, strong) NSArray *analogChannelData; // of NSNumber, 128 elements
#end
(I have stripped other properties of the class not relevant to the question)
So for example, sample[1970] could be:
sample.timeStamp = 970800
sample.analogChannelData = <NSArray>
[
[0] = #(153.27)
[1] = #(345.35)
[2] = #(701.02)
...
[127] = #(-234.45)
]
Where each element [i] in the analogChannelData represents the value of that specific channel i for the timeStamp 970800
Now I want to obtain the maximum value for all the 4000 samples for channel 31. I use the following code:
NSUInteger channelIndex = 31;
NSMutableArray *values = [[NSMutableArray alloc] init]; // of NSNumber
// iterate the array of samples and for each one obtain the value for a
// specific channel and store the value in a new array
for (CMData *sample in samples) {
[values addObject:sample.analogChannelData[channelIndex]];
}
// the maximum
NSNumber *maxValue = [values valueForKeyPath:#"#max.self"];
I want to replace this programming structure by a filter through an NSPredcicate or use valueForKeyPath: to obtain the maximum of the data I need.
Anyone knows how to do this without a for loop? Just using NSPredicates and/or valueForKeyPath?
Thank you very much in advance for your help.
Update 1
Finally I benckmarked the for-loop version against the keyPath version (see accepted answer) and it runs much faster so it is better to go with a for loop.
Recalling some lessons from my algorithms classes, I implemented an even faster version that doesn't need an array to store the values. I just iterate over the selected channel and just choose the maximum in each iteration. This is by far the fastest version.
So:
version 1: for loop (see code above)
version 2: version with custom property (see selected answer from Marcus, update 2)
version 3: new code
Code for version 3:
NSUInteger channelIndex = 31;
NSNumber *maxValue = #(-INFINITY);
for (CMTData *sample in samples) {
NSNumber *value = sample.analogChannelData[channelIndex];
if (value) { // I allow the possibility of NSNull values in the NSArray
if ([value compare:maxValue] == NSOrderedDescending)
maxValue = value;
}
}
// the maximum is in maxValue at the end of the loop
Performance:
After 20.000 iterations in iOS simulator:
Version 1: 12.2722 sec.
Version 2: 21.0149 sec.
Version 3: 5.6501 sec.
The decision is clear. I'll use the third version.
Update 2
After some more research, it is clear to me now that KVC does not work for infividual elements in the inner array. See the following links: KVC with NSArrays of NSArrays and Collection Accessor Patterns for To-Many Properties
Anyway because I wanted to compute the maximum of the elements it is better to iterate the array than use some tricks to make KVC work.
You can solve this with using Key Value Coding and the collection operators.
NSNumber *result = [sample valueForKeyPath:#"#max.analogDataChannel"];
Update 1
As Arcanfel mentioned, you can join the arrays together:
NSNumber *result = [samples valueForKeyPath:#"#max.#unionOfArrays.#analogChannelData"];
I would suggest reading the documentation that we both linked to. There are some very powerful features in there.
Update 2
Further to HRD's answer, he has your solution, you need to combine his changes with KVC.
Add a propert to your CMData object for currentChannel. Then you can call
[samples setValue:#(channelIndex) forKey:#"currentChannel"];
Which will set it in every instance in the array. Then call:
[samples valueForKeyPath:#"#max.analogDataForCurrentChannel"];
Then you are done.
I have not tested out the code yet, but I think this is exactly what you are looking for:
[samples valueForKeyPath:#"#max.(#unionOfArrays.analogChannelData)"];
I guess you can also use #distinctUnionOfArray to remove duplicate values.
Here is the link to Apple Documentation that covers collection operators.
Hope this is helpful!
Cheers!
A suggestion for further exploration only
Offhand it is not clear you can do this as-is with a single KVC operator. What you might consider is adding two properties to your class: currentChannel, which sets/gets the current channel; and analogChannelDataForCurrentChannel, which is equivalent to analogChannelData[currentChannel]. Then you can:
samples.currentChannel = channelIndex;
... [samples valueForKeyPath:"#max.analogChannelDataForCurrentChannel"];
with any appropriate locking between the two calls if thread-safety is required (so one thread does not set currentChannel, then a second, and then the first do the KVC operator with the second's channel...).
HTH

using weak qualifier for variable in a method

- (void)showWeakValue
{
NSString * __weak weakString = [[NSString alloc] initWithFormat:#"First Name: AA"];
NSNumber * __weak weakNum = [[NSNumber alloc] initWithInt:10];
NSLog(#"weakString = %#", weakString);
NSLog(#"weakNum = %#",weakNum);
}
the output is
weakString = (null)
weakNum = 10
why is weakNum not null? because there's no other strong reference to weakNum, it should be deallocated immediately after assignment. right?
NSNumber is toll-free bridged to CFNumber, which is open source.
From http://www.opensource.apple.com/source/CF/CF-476.19/CFNumber.c
you can see that NSNumber caches the created objects for integer values between
(-1) and 12, so that the object returned by
[[NSNumber alloc] initWithInt:10]
is never deallocated, and multiple calls return the same instance.
For
[[NSNumber alloc] initWithInt:1000]
you will probably see a different behaviour.
I think either weakNum is deallocated immediately (as expected) and your code then invokes undefined behavior, or NSNumber is making tricky optimizations and you get back a singleton which always holds the value 10 and is alive throughout the lifetime of the program.
Edit: aham, huh, nope. I just logged the pointer itself using
NSLog(#"weakNum pointer: %016llx", (uint64_t)weakNum);
and got
weakNum pointer: 000000000003e883
Now that's weird. A pointer-to-struct which is not 16 byte-aligned? So this is a tagged pointer.
Let's analyze the pointer value! The 0th (LSB) bit is set, that's clear - that indicates the tagged pointer.
The 1st bit is set too: according to the reference behind the link, that's the tagged object class for integers. That matches our expectations. That's the 0th nibble.
Then in the next (1st) nibble, i. e. bits 4...7, we have the value 8, which is binary 100, for which the quoted reference says 0100 for 16-bit integers. That's exact, 1000 doesn't fit into 8 bits, but it does fit into 16 bits.
And then what do we have next? 3e8, which is the hexadecimal for decimal 1000.
So, we've proved that this is a tagged pointer - at least on the system I tested this on (OS X 10.7.5 64-bit).

How to determine when the value pointed to by a pointer is nil

I have a situation where troops can attack buildings. Each troop keeps a pointer to its target.
#property (nonatomic, weak) Building *target;
In an update loop, the troops periodically cause damage to their target.
if (_target)
{
if (/*enough time has passed since last attack, attack again*/)
{
[_target attack];
if (_target.health <= 0)
{
[_target removeFromParentAndCleanup:YES]; //Cocos2d
_target = nil;
}
}
}
else /* Find new target */
The problem is:
troop1 deals the blow that fells building1 and moves on to building2
troop2 was attacking building1 but waits until its next attack to determine that building1 is now nil.
I realise the problem is that troop2's pointer has not been set to nil and instead I should be checking that the value of the pointer is nil.
I tried using if (*_target) but was met with the message
Statement requires expression of scalar type
If there a way to achieve this kind of comparison in Objective-C? What other options are there for determining when a value has changed? KVO? Some extensive delegate pattern?
It is the pointer itself that is set to nil when the object it points to is deallocated. if (objectPointer == nil) is always the way to check if an object is nil in Objective-C/Cocoa. If the pointer is not nil, it means the object in question has not in fact been deallocated. If you dereference a pointer to an object, you get a struct, hence the compiler error about needing a scalar value in the if expression.
So, in your case, if if(self.target != nil) is not giving you the result you expect, you should look for remaining strong references to the target (from other objects).
More broadly, as hinted at by trojanfoe's answer, you're relying on ARC's zeroing weak reference behavior for real program logic. In theory this is OK, as (contrary to his initial statement), ARC's zeroing weak behavior is reliable/deterministic. But, it does mean that you have to ensure that targets are always deallocated when they're no longer on the playing field (or whatever). This is a bit fragile. Zeroing weak references are intended as a way to avoid retain cycles (essentially a form of memory leak), rather than as a way to implement logic the way you're doing. The gist of trojanfoe's solution, where you explicitly register and unregister targets as necessary, is probably a more robust solution.
There may be something that I have overlooked here, but to check if the target2 property is nil, just do:
if ( self.target2 == nil ) {
// Something
}
I think you are relying too heavily on the implementation of ARC in that you only know if an object has been removed if the pointer is nil. This is non-portable and can you make any guarantee between the object being released and the pointer becoming nil?
Instead, use a central dictionary of objects, mapped against their unique ID and store just this unique ID rather than the object pointer itself. In this example I'm using a NSNumber for the key using an incrementing integer, but there are probably better keys that can be used. Also Object is the base class of any object you want to store in this dictionary:
// Probably ivars in a singleton class
unsigned _uniqueId = 1;
NSMutableDictionary *_objects;
- (NSNumber *)addObject:(Object *)object
{
NSNumber *key = [NSNumber numberWithUnsignedInt:_uniqueId++];
[_objects setObject:object forKey:key];
return key;
}
- (void)removeObjectForKey:(NSNumber *)key
{
[_objects removeObjectForKey:key];
}
- (Object *)getObjectForKey:(NSNumber *)key
{
return [_objects objectForKey:key];
}
And in your target, simply store the building key:
#property (strong) NSNumber *buildingKey;
and get the building via the methods provided:
Building *building = (Building *)[objectDictionary objectForKey:buildingKey];
if (building != nil)
{
// building exists
}
else
{
// building does not exist; throw away the key
buildingKey = nil;
}
Since target is a weak reference, your code should work "as-is", assuming that [_target removeFromParentAndCleanup:YES]; removes all strong references to the target.
When the last strong reference is removed, all of the weak properties pointing to it will automatically be set to nil. If they are not automatically set to nil, then there is still a strong reference to the target somewhere.
Find and remove that reference, and this will work fine.

Resources