CFBridgingRelease of a CFArray containing CFString - ios

Suppose I have a CFArray containing CFString's:
CFStringRef strs[3];
CFArrayRef anArray;
strs[0] = CFSTR("String One");
strs[1] = CFSTR("String Two");
strs[2] = CFSTR("String Three");
anArray = CFArrayCreate(NULL, (void *)strs, 3, &kCFTypeArrayCallBacks);
If I use CFBridgingRelease to cast the CFArrayRef into an NSArray * (and in the process transfer the ownership of the array object to ARC), then does each element of the original array also get a CFBridgingRelease call automatically? It seems like, once I do:
NSArray * arrayInArc = CFBridgingRelease(anArray);
I can treat the elements of the NSArray as NSString's without having explicitly called CFBridgingRelease on each of the original CFStringRef:
NSString * a0 = arrayInArc[0];
Is there any documentation saying that when you transfer of the ownership of a collection (e.g., CFArray) to ARC, the ownership of its elements are also transferred?
Thanks,

the CFBridingRelease doesn't change the type of the array.. CFArrayRef <> NSArray* is the same even before, It just tells the compiler to manage retain/release calls for you
how the array releases its contents is not affected by it.
YOU don't have references to the content, the array does! And the CFArrayRef/NSArray* manages the retain/release calls with/without arc.
the basic idea: only care about releasing stuff YOU own. (In this case the array itself)

Related

How to understand ARC in objc

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;
}

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.

C-style array of pointers to Objective-C objects under ARC

I have a 2D array of pointers to Objective-C instances to keep track of game objects on a map grid.
Now I am transitioning my code to ARC, and Xcode pointed the error. I knew pointers to objects aren't allowed as struct members, but this one caught me (almost) off guard.
I understand the rationale behind the ARC constrains, but:
I can't afford the overhead of objective-C arrays when looking up objects in the grid, and
The objects themselves are already owned by an NSArray ivar defined in the same class that has the C-style grid as an ivar; the c-style array is only a conveniently structured shortcut. Futhermore, when objects are removed from the owning NSArray, I set the corresponding grid slot to NULL.
That is, the 2D array (grid) is just a collection of fast (but dumb) pointers to objects safely retained somewhere else (the NSArray ivar).
Is there a way to get away with this using casts? For example, define and alloc my grid as:
void*** _grid;
instead of
MyMapObjectClass*** _grid
and use (appropriately bridged) casts between void* <-> MyMapObjectClass* when setting or getting the pointers in each slot?
EDIT: So here is how I solved it
I changed the ivar declaration as described above. In addition, when setting an entry of my look-up grid, I did this:
// (Done **Only Once** at map initialization)
// _objectArray is an instance of NSMutableArray
MyMapObjectClass* mapObject = [[MyMapObjectClass alloc] init];
// ...configure map object, etc...
// Add to Obj-C array:
[_objectArray addObject:mapObject];
// Add pointer to 2D C array:
_grid[i][j] = (__bridge void*)mapObject;
When accessing the object at (x,y), I do the opposite:
MyMapObjectClass* object = (__bridge MyMapObjectClass*) _grid[x][y];
[object performSomeMethod];
// etc...
When removing the object from the map, I do this:
MyMapObjectClass* object = (__bridge MyMapObjectClass*) _grid[x][y];
[_objectArray removeObject:object];
_grid[x][y] = NULL;
Map objects are created once at the beginning of the game, and removed according to game progress. If I need to replace a map object for another, I would do this:
MyMapObjectClass* oldObject = (__bridge MyMapObjectClass*) _grid[x][y];
// (should mark as weak?)
[_objectArray removeObject:oldObject];
_grid[x][y] = NULL;
MyMapObjectClass* newObject = [[MyMapObjectClass alloc] init];
[_objectArray addObject:newObject];
_grid[x][y] = (__bridge void*)newObject;
Circumventing ARC using casts is generally a bad idea. The better way would be to disable ARC for your map.m (or break out just the lookup part into a separate class).Then do manual memory management inside it with retain / release and the C structures you like, as long as you do it correctly it will work fine and you will be able to call it from other classes, avoiding the overhead of nested NSArrays etc..

Allocating a NSUInteger IndexBuffer

I am trying to retrieve my NSIndexSet by calling -getIndexes:maxCount:indexRange
Here is my code
const NSUInteger arrayCount = picturesArray.count;
NSUInteger theIndexBuffer[arrayCount];
[picturesArray getIndexes:theIndexBuffer maxCount:arrayCount inIndexRange:nil];
However, in the debugger the IndexBuffer is always showing as -1. When I initialize it with a static number like NSUInteger theIndexBuffer[10], I get a proper instance.
This is probably because it doesnt know what arrayCount is at compile time.
What is the correct way of doing this?
You can dynamically allocate it using a pointer like this:
NSUInteger *theIndexBuffer = (NSUInteger *)calloc(picturesArray.count, sizeof(NSUInteger));
// Use your array however you were going to use it before
free(theIndexBuffer); // make sure you free the allocation
In C, even though this is a dynamically allocated buffer, you can use the array subscript operators and (for the most part) pretend that it's just an array. So, if you wanted to get the third element, you could write:
NSUInteger thirdElement = theIndexBuffer[2]; // arrays are 0-indexed
Just don't forget to free it when you're done.
I don't think you did anything wrong. It's valid in C99. You can double check by seeing if this evaluates as true:
sizeof(theIndexBuffer) == arrayCount * sizeof(NSUInteger)

Resources