Passing an object around increases retain count - ios

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.

Related

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.

CFBridgingRelease of a CFArray containing CFString

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)

How to release instance variable in ARC - Objective-C

I know the instance variable in ARC are by default __strong. How can I release an instance variable when the containing class is still retained. In the following example v is __strong
and c is allocated when object of A is created some where and retained. I want to release the
c instance variable. How to should I do that?, What should be in releaseC method that will release the c instance variable.
#interface A {
Obj *c;
}
#implementation A {
- (id)init {
if((self = [super init])){
c = [[Obj alloc] init];
}
return self;
}
- (void)releaseC {
//what should be here?
}
}
Obj *c; = [[Obj alloc] init];
- (void)releaseC {
c = nil;
}
You cannot directly control when an object is released BUT you can indirectly cause it to happen. How? Remember what ARC does EXACTLY. Unlike human coding convention, ARC parses your code and inserts release statements AS SOON AS OBJECTS CAN be released. This frees up the memory for new allocations straight away, which is awesome/necessary. Meaning, setting an object to nil, or simply allowing a variable to go out of scope ... something that CAUSES A 0 RETAIN COUNT forces ARC to place its release calls there. It must ... because it would leak otherwise.
- (void)releaseC {
c = nil;
}
c = nil;
But some would argue it isn't productive from an efficiency standpoint. And while the release will be immediate in the sense it isn't any longer usable, the memory may not be freed immediately.
there is no need to release the variable in ARC. it done automatically
You are probably miss understanding what you want to do. I suppose you want to release the variable for memory issues. All you have to do is nil it. Instance variables are pointers to objects. As long as an object is pointed by something it is kept alive. As soon as you dont need something you can "stop pointing at it" and it will be released automagically.
As for the design, I am not so sure why you would have a public method that releases an instance variable. (I'm assuming its public because if it was not you would just nil it without actually having to write a method). If you do indeed intend to be able to release an instance variable from outside the class, I would simply make the Instance variable public and release it from anywhere setting it as nil.

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.

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

Resources