Can you please tell me which is the right way and why in non ARC world.
+ (NSString *)getUUID {
CFUUIDRef theUUID = CFUUIDCreate(NULL);
CFStringRef string = CFUUIDCreateString(NULL, theUUID);
CFRelease(theUUID);
return [(NSString*) string autorelease];
}
or
+ (NSString *)getUUID {
CFUUIDRef theUUID = CFUUIDCreate(NULL);
CFStringRef string = CFUUIDCreateString(NULL, theUUID);
CFRelease(theUUID);
return (NSString*)string;
}
The other answers are correct for manual retain counting. When you come to your senses ;^) and switch to ARC, you won't be able to send autorelease. Instead, under ARC, do it this way:
+ (NSString *)getUUID {
CFUUIDRef theUUID = CFUUIDCreate(NULL);
CFStringRef string = CFUUIDCreateString(NULL, theUUID);
CFRelease(theUUID);
return CFBridgingRelease(string);
}
A CFBridgingRelease is equivalent to a CFRelease for the purposes of balancing the +1 retain count returned by CFUUIDCreateString, but also returns a still-valid reference that ARC will take care of releasing.
CFStrings do need to be released. The first way is correct because CFString is toll-free bridged with NSString, and thus can safely be autoreleased like an NSString.
Your method should return an autoreleased object so the client is responsible for obtaining ownership over the object, and it won't leak if they ignore the return value.
Number one is the correct method. The CFString is created, +1 retain count, autoreleased and returned for the client. The cast to NSString does not affect the retain count.
In the second method the CFString is created, +1 retain count, but never balanced with a release or autorelease.
Related
I am getting following error but how to resolve it ?
Error is highlighted with green circle "Reference counted object is used after it is released"
Edited: I am using following method
+ (NSString *)GetUUID
{
CFUUIDRef theUUID = CFUUIDCreate(NULL);
CFStringRef string = CFUUIDCreateString(NULL, theUUID);
CFRelease(theUUID);
NSString *str = (__bridge NSString *)string;
CFRelease(string);
return str;
}
Edited: Resolved by using vijay's following simple code
NSUUID *UUID = [NSUUID UUID];
NSString* stringUUID = [UUID UUIDString];
I hope, you are getting this error because of [DBManager GetUUID] method, where you would release the CFRelease(cfUuid).
To get the UUID, try this simplified API
+ (NSString *)GetUUID
{
NSUUID *UUID = [NSUUID UUID];
NSString* stringUUID = [UUID UUIDString];
return stringUUID;
}
After CFUUIDCreateString, you get a string you own. By using __bridge, you set str to the same string. So when you CFRelease(string) you do not own the memory backing str anymore...
To avoid this, either use a Cocoa method like #vijay says, or remove the CFRelease and use __bridge_transfer NSString* instead of __bridge. This tells the compiler you're transferring a CF object you own into the ARC world.
Per the documentation:
__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.
Here's my code:
- (void)peripheralManager:(CBPeripheralManager *)peripheralManager central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic
{
[self.centralManager retrievePeripherals:#[central.UUID]];
}
I get an error:
Collection element of type 'CFUUIDRef' (aka 'const struct __CFUUID *') is not an Objective-C object
What do I do?
The problem:
The compiler only knows about types, not about runtime behavior. It doesn't know that - most probably - CFUUIDRef can be used just like any normal Objective-C object (although it doesn't officially have a toll-free bridged Foundation class counterpart). It only sees that const struct __CFUUID is not an Objective-C class, and it bails out.
The solution:
I. I presume this will work - just tried it and it indeed works, CFUUID even has a nice description when printed using NSLog(). However, it is not documented. Just cast it to id, like this:
#[(__bridge id)central.UUID]
II. Yes, you can convert it to a string, but that won't make the compiler error go away either - you do need that typecast, because the compiler quirks about the incompatible types:
CFStringRef uuidString = CFUUIDCreateString(kCFAllocatorDefault,#[central.UUID]);
NSString *uuidNSString = (__bridge NSString *)uuidString;
Now this is guaranteed to work.
My recommendation would be to convert it to an NSString and add that to the array.
+ (NSString *)convertUUID:(CFUUIDRef)theUUID
{
CFStringRef string = CFUUIDCreateString(NULL, theUUID);
CFRelease(theUUID);
return (__bridge_transfer NSString *)string;
}
Try converting it to a Objective-C Object:
CFStringRef uuidString = CFUUIDCreateString(kCFAllocatorDefault, yourUUID);
NSString *uuidNSString = (__bridge NSString *)uuidString;
And if you need it back:
CFUUIDRef uuid = CFUUIDCreateFromString(kCFAllocatorDefault, uuidString);
Warning is:
Potential leak of an object stored into 'escaped_value'
Here is the code:
- (NSURL*)generateURL:(NSString*)baseURL params:(NSDictionary*)params {
if (params) {
NSMutableArray* pairs = [NSMutableArray array];
for (NSString* key in params.keyEnumerator) {
NSString* value = params[key];
NSString* escaped_value = (__bridge NSString *)CFURLCreateStringByAddingPercentEscapes(
NULL, /* allocator */
(__bridge CFStringRef)value,
NULL, /* charactersToLeaveUnescaped */
(CFStringRef)#"!*'();:#&=+$,/?%#[]",
kCFStringEncodingUTF8);
[pairs addObject:[NSString stringWithFormat:#"%#=%#", key, escaped_value]];
}
NSString* query = [pairs componentsJoinedByString:#"&"];
NSString* url = [NSString stringWithFormat:#"%#?%#", baseURL, query];
return [NSURL URLWithString:url];
} else {
return [NSURL URLWithString:baseURL];
}
}
You are creating a string when you call CFURLCreateStringByAddingPercentEscapes and never CFReleasing it. Either CFRelease the object before it goes out of scope or change the __bridge to __bridge_transfer to let ARC take care of it.
__bridge_transfer tells ARC that during the cast that a +1 retain count on the object is being transferred to be under ARCs responsibility. ARC will release the instance according to its rules.
__bridge tells ARC that retain counts are not being transferred during the cast.
- (NSString*)encodeURL:(NSString *)string
{
NSString *newString = (__bridge NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (__bridge CFStringRef)string, NULL, CFSTR(":/?#[]#!$ &'()*+,;=\"<>%{}|\\^~`"), CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding));
if (newString)
{
return newString; // <-- potential leak here
}
return #"";
}
I'm not familiar with CFTypes (other than knowing what they are). This is code I got from the internet and have had to finagle a bit to get it to work in ARC. I'm getting a potential leak warning and I'm not sure how to fix it. Suggestions?
Yes, this is a memory leak. You meant to use CFBridgingRelease() rather than __bridge.
The object created by CFURLCreateStringByAddingPercentEscapes has an extra retain on it because it includes Create. You need to transfer that object to ARC, letting it know to add an extra release, that's what CFBridgingRelease() does.
NSString *newString =
CFBridgingRelease(
CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
(__bridge CFStringRef)string,
NULL,
CFSTR(":/?#[]#!$ &'()*+,;=\"<>%{}|\\^~`"),
kCFStringEncodingUTF8));
You do use __bridge for the passed-in string because you're not transferring it to Core Foundation. You're just asking Core Foundation to use it while ARC continues to own it. When you "transfer" ownership, you generally mean "this object used to be Core Foundation, and now it's ARC" (or vice versa). That's what's happening to newString.
I swapped the long NS-to-CF encoding function with the result just to make it shorter.
return a autoreleased version of the string, CFStringRef can be converted to NSString and vice versa, which also means that you can treat it like a normal NSString and thus autorelease it (if you are interested in this you should read about the toll free bridging between parts of Foundation and Core Foundation)
Another way is using __bridge_transfer instead of __bridge like this:
NSString *newString = (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (__bridge CFStringRef)string, NULL, CFSTR(":/?#[]#!$ &'()*+,;=\"<>%{}|\\^~`"), CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding));
See more informations in this question: Correct bridging for ARC?
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.