I need to generate a UUID string in some code with ARC enabled.
After doing some research, this is what I came up with:
CFUUIDRef uuid = CFUUIDCreate(NULL);
NSString *uuidStr = (__bridge_transfer NSString *)CFUUIDCreateString(NULL, uuid);
CFRelease(uuid);
Am I correctly using __bridge_transfer to avoid leaking any objects under ARC?
Looks fine to me. This is what I use (available as a gist)
- (NSString *)uuidString {
// Returns a UUID
CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
NSString *uuidString = (__bridge_transfer NSString *)CFUUIDCreateString(kCFAllocatorDefault, uuid);
CFRelease(uuid);
return uuidString;
}
Edited to add
If you are on OS X 10.8 or iOS 6 you can use the new NSUUID class to generate a string UUID, without having to go to Core Foundation:
NSString *uuidString = [[NSUUID UUID] UUIDString];
// Generates: 7E60066C-C7F3-438A-95B1-DDE8634E1072
But mostly, if you just want to generate a unique string for a file or directory name then you can use NSProcessInfo's globallyUniqueString method like:
NSString *uuidString = [[NSProcessInfo processInfo] globallyUniqueString];
// generates 56341C6E-35A7-4C97-9C5E-7AC79673EAB2-539-000001F95B327819
It's not a formal UUID, but it is unique for your network and your process and is a good choice for a lot of cases.
That looks correct to me.
You have CFRelease'd uuid, which is your responsibility from the CFUUIDCreate()
And you've transferred ownership of the string to ARC, so the compiler knows to release uuidStr at the appropriate time.
From clang docs:
(__bridge_transfer T) op casts the operand, which must have non-retainable pointer type, to the destination type, which must be a retainable object pointer type. ARC will release the value at the end of the enclosing full-expression, subject to the usual optimizations on local values.
So you are doing it right.
Related
I am trying to understand the correct way of getting an NSString from a CFStringRef in ARC?
Same for going the opposite direction, CFStringRef to NSString in ARC?
What is the correct way to do this without creating memory leaks?
Typically
NSString *yourFriendlyNSString = (__bridge NSString *)yourFriendlyCFString;
and
CFStringRef yourFriendlyCFString = (__bridge CFStringRef)yourFriendlyNSString;
Now, if you want to know why the __bridge keyword is there, you can refer to the Apple documentation. There you will find:
__bridge transfers a pointer between Objective-C and Core Foundation with no transfer of ownership.
__bridge_retained or CFBridgingRetain casts an Objective-C pointer to a Core Foundation pointer and also transfers ownership to you.
You are responsible for calling CFRelease or a related function to relinquish ownership of the object.
__bridge_transfer or CFBridgingRelease moves a non-Objective-C pointer to Objective-C and also transfers ownership to ARC.
ARC is responsible for relinquishing ownership of the object.
Which means that in the above cases you are casting the object without changing the ownership.
This implies that in neither case you will be in charge of handling the memory of the strings.
There may also be the case in which you want to transfer the ownership for some reason.
For instance consider the following snippet
- (void)sayHi {
CFStringRef str = CFStringCreateWithCString(NULL, "Hello World!", kCFStringEncodingMacRoman);
NSString * aNSString = (__bridge NSString *)str;
NSLog(#"%#", aNSString);
CFRelease(str); //you have to release the string because you created it with a 'Create' CF function
}
in such a case you may want to save a CFRelease by transferring the ownership when casting.
- (void)sayHi {
CFStringRef str = CFStringCreateWithCString(NULL, "Hello World!", kCFStringEncodingMacRoman);
NSString * aNSString = (__bridge_transfer NSString *)str;
// or alternatively
NSString * aNSString = (NSString *)CFBridgingRelease(str);
NSLog(#"%#", aNSString);
}
The ownership of str has been transferred, so now ARC will kick in and release the memory for you.
On the other way around you can cast a NSString * to a CFString using a __bridge_retained cast, so that you will own the object and you'll have to explicitly release it by using CFRelease.
To wrap it up you can have
NSString → CFString
// Don't transfer ownership. You won't have to call `CFRelease`
CFStringRef str = (__bridge CFStringRef)string;
// Transfer ownership (i.e. get ARC out of the way). The object is now yours and you must call `CFRelease` when you're done with it
CFStringRef str = (__bridge_retained CFStringRef)string // you will have to call `CFRelease`
CFString → NSString
// Don't transfer ownership. ARC stays out of the way, and you must call `CFRelease` on `str` if appropriate (depending on how the `CFString` was created)
NSString *string = (__bridge NSString *)str;
// Transfer ownership to ARC. ARC kicks in and it's now in charge of releasing the string object. You won't have to explicitly call `CFRelease` on `str`
NSString *string = (__bridge_transfer NSString *)str;
void CountlyRecordEventSegmentationCountSum(const char * key, const char * segmentation, int count, double sum)
{
NSString * seg = CreateNSString(segmentation);
NSArray * entries = [seg componentsSeparatedByString:#"`"];
NSDictionary * dict = [NSDictionary dictionary];
for (id entry in entries)
{
NSArray * keyValue = [entry componentsSeparatedByString:#"|"];
[dict setValue:[keyValue objectAtIndex:1] forKey:[keyValue objectAtIndex:0]];
}
[[Countly sharedInstance] recordEvent:CreateNSString(key) segmentation:dict count:count sum:sum];
}
I put "?" in the title because I'm not entirely sure if the problem is in the code above but that's my best guess. I'm integrating Countly iOS plugin with Unity and one of Countly plugin's methods take NSDictionary * as argument. As I don't know how to send a dictionary from C# to Objective-C I'm storing my dict in a string, sending it to Objective-C and then recreating the dictionary (the code above).
But that's probably even not relevant. I know EXC_BAD_ACCESS usually has something to do with unfreed resources or sth so maybe you can see what I'm doing wrong (I don't know Objective-C at all, just writing a few lines needed by the plugin).
Edit:
From Unity sample:
// Converts C style string to NSString
NSString * CreateNSString (const char * string)
{
if (string)
return [NSString stringWithUTF8String: string];
else
return [NSString stringWithUTF8String: ""];
}
The error you've made is that you are trying to modify immutable version of NSDictionary.
One cannot modify contents of the NSDictionary after it's initialization. You should use NSMutableDictionary instead.
Here is a documentation on NSMutableDictionary.
And here is an example of how to create mutable version of an immutable object that conforms to NSMutableCopying protocol.
You need to be using NSMutableDictionary, you can't modify an NSDictionary.
Also, you should use setObject:forKey: because setValue:forKey: is a KVC method. It happens to do the same thing on an NSMutableDictionary for most keys, but it is marginally slower.
Finally, you should check that [keyValue count] >= 2 before trying to access the objects at indexes 0 and 1.
Edit Also, CreateNSString() looks suspicious. It might be either leaking or prematurely releasing the string. But you need to post the code. In any case, I'd use
seg = [NSString stringWithUTF8String: segment];
or, other appropriate method if segment is not encoded in UTF-8.
Actually I am working on a project with ARC enabled. I know using alloc and init is taking ownership of the object. I know, If I create a string like this
NSString *myString = [[NSString alloc]initWithFormat:#"Something"];
then I need to release the myString on myself. What If I am using ARC enabled? I cannot release on myself. So will it create a leak? Or should I don't create object like this?
I can create a string like below code too.
NSString *myString = [NSString stringWithFormat:#"Something"];
But which type I need to use exactly for ARC enabled project ? What will happen if I use first type?
If you use ARC, all the necessary release calls will be added for you when you compile. It will not leak.
The difference between
NSString *myString = [[NSString alloc]initWithFormat:#"Something"];
and
NSString *myString = [NSString stringWithFormat:#"Something"];
is that the first one will automatically released after the last reference to myString in that block, while the second one is an autoreleased instance that will only be released at the end of the run loop. It's not a big difference, but if you're using a lot of objects, try to avoid autoreleased ones to keep memory usage low.
ARC takes care of the memory management, so no you don't need to worry about calling release on your myString variable, ARC will do that for you. Also as a suggestion I would recommend using convenience methods to create your object such as
[NSString stringWithFormat:#"Something"];
It's enough to set the string pointer to nil to release it.
You can also do the same things that you would be able to do without ARC, but with the advantage that if you don't explicitly do anything, the ARC will manage (almost) everything for you.
So to release it you set it to nil, let's see what else you could do:
NSString* str= [[NSString alloc]initWithUTF8String: "Hello"];
// here the retain count of str is 1
__unsafe_unretained NSString* string= str;
// again 1 because string is __unsafe_unretained
void* data= (__bridge_retained void*) string;
// data retains the string, so the retain count is to 2
// This is useful in the case that you have to pass an objective-c object
// through a void pointer.You could also say NSString* data= string;
str=nil;
// Here the retain count of str is 1
NSLog(#"%#",(__bridge NSString*)data);
UPDATE
Here's why sometimes you don't notice that an object is released:
NSString* str= [[NSString alloc]initWithString: #"hey"];
__unsafe_unretained NSString* str2=str;
str=nil;
NSLog(#"%#",str2);
In this case str=[[NSString alloc]initWithString: #"hey"] is equal to str=#"hey", with the difference that str is autoreleased and not released.But the compiler optimizes the code in str=#"hello", so if you are inside an autorelease block you won't have any problem, str2 will be printed correctly.
That's why I used initWithUTF8String, to avoid that compiler optimization.
I am creating a NSString like this, should I release it afterwards or not?
CFUUIDRef uuidObj = CFUUIDCreate(nil);
NSString *device = (NSString*)CFUUIDCreateString(nil, uuidObj);
CFRelease(uuidObj);
Thanks!
Here is documentation for this function:
CFUUID Reference - CFUUIDCreateString
and it states that ownership follows The Create Rule.
Here are some more links with answer:
Memory management with CF Objects
Strings from UUID
CFUUIDRef theUUID = CFUUIDCreate(NULL);
NSString *s2ndUuid = (__bridge_transfer NSString*)CFUUIDCreateString(kCFAllocatorDefault, theUUID);
I am trying to convert a CFUUIDRef to a NSString *.
Before, I used the following code, and worked fine.
CFStringRef str = CFUUIDCreateString(NULL, _uuid); # _uuid is of type CFUUIDRef
return (__bridge NSString *) str;
However, after a recent update on Xcode (or other thing that I didn't notice?), the above code gives me the error:
Use of undeclared identifier '__bridge'
So have I did something wrong? How could I solve it?
=== UPDATED ===
The full code:
+ (NSString *)uuidToString:(CFUUIDRef)_uuid {
CFStringRef str = CFUUIDCreateString(NULL, _uuid); # _uuid is of type CFUUIDRef
return (__bridge NSString *) str;
}
The uuid is generated by:
uuid = CFUUIDCreate(NULL);
__bridge is only defined with ARC (Automatic Reference Counting) enabled. It is used to "transfer objects in and out of ARC control". (Source)
To turn on ARC, go to your build settings and set Objective-C Automatic Reference Counting to Yes.
Or, if you do not want to use ARC, simply remove __bridge and it should work fine.