NSMakeCollectable and ARC doesn't work

2019-01-23 23:46发布

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?

5条回答
forever°为你锁心
2楼-- · 2019-01-24 00:12
CFUUIDRef theUUID = CFUUIDCreate(NULL);

NSString *s2ndUuid = (__bridge_transfer NSString*)CFUUIDCreateString(kCFAllocatorDefault, theUUID);
查看更多
叼着烟拽天下
3楼-- · 2019-01-24 00:22

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.

查看更多
放荡不羁爱自由
4楼-- · 2019-01-24 00:24

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;
}
查看更多
Ridiculous、
5楼-- · 2019-01-24 00:26

it's definition of NSMakeCollectable

NS_INLINE id NSMakeCollectable(CFTypeRef cf) {
   return cf ? (id)CFMakeCollectable(cf) : nil;
}

I think this should work

uuid =CFUUIDCreateString(kCFAllocatorDefault, theUUID);
查看更多
趁早两清
6楼-- · 2019-01-24 00:34

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.

查看更多
登录 后发表回答