Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSMakeCollectable and ARC doesn't work

I'm trying to convert my old project to ARC. I have a function which creates UUIDs, but apparently this is no longer supported when using ARC:

NSString *uuid = nil;
    CFUUIDRef theUUID = CFUUIDCreate(kCFAllocatorDefault);
    if (theUUID) {
        uuid = NSMakeCollectable(CFUUIDCreateString(kCFAllocatorDefault, theUUID));
        //[uuid autorelease];
        CFRelease(theUUID);
    }

I get the compiler error (when trying to convert): 'NSMakeCollectable' is unavailable: not available in automatic reference counting mode.

So my question is: how do I create UUIDs when using ARC? Is there another way which I should now use?

like image 702
n.evermind Avatar asked Dec 21 '11 18:12

n.evermind


4 Answers

NSMakeCollectable() is for the benefit of the (essentially deprecated) Objective-C garbage collector. ARC knows nothing about it.

You must use a special casting attribute, usually __bridge_transfer, to ensure that the memory is not leaked. __bridge_transfer is used like so:

id MakeUUID(void) {
    id result = nil;
    CFUUIDRef uuid = CFUUIDCreate(NULL);
    if (uuid) {
        result = (__bridge_transfer id)uuid; // this "transfers" a retain from CF's control to ARC's control.
    }
    return result;
}

Edit: As other answers have mentioned, CFBridgingRelease() does this for you. So instead of using (__bridge_transfer id)uuid, it may be cleaner to write CFBridgingRelease(uuid). They are equivalent though, so it's up to you which you find more readable.

like image 137
Jonathan Grynspan Avatar answered Nov 09 '22 16:11

Jonathan Grynspan


When you transfer the object from CFString to NSString, you need to let ARC know how you want to handle memory management. In this case I would suggest:

uuid = CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, theUUID));

This instructs CoreFoundation to release the object (as is required to balance Create). Cocoa will ARC-retain the object when it is assigned to uuid.

like image 41
Rob Napier Avatar answered Nov 09 '22 14:11

Rob Napier


CFUUIDRef theUUID = CFUUIDCreate(NULL);

NSString *s2ndUuid = (__bridge_transfer NSString*)CFUUIDCreateString(kCFAllocatorDefault, theUUID);
like image 1
user1086279 Avatar answered Nov 09 '22 14:11

user1086279


To have a single UUID across whole app, I think the best way to achieve that would be to have it run once in the whole application lifecycle.

static NSString *uuid;
- (NSString *)theCurrentDeviceUniqueIdentifier {

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        CFUUIDRef theUUID = CFUUIDCreate(kCFAllocatorDefault);
        if (theUUID) {
            uuid = CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, theUUID));
            CFRelease(theUUID);
        }
    });

    return uuid;
}
like image 1
Mohammad Abdurraafay Avatar answered Nov 09 '22 15:11

Mohammad Abdurraafay