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?
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.
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.
CFUUIDRef theUUID = CFUUIDCreate(NULL);
NSString *s2ndUuid = (__bridge_transfer NSString*)CFUUIDCreateString(kCFAllocatorDefault, theUUID);
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;
}
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);
Related
I want to get the unique identifier which should support all iOS versions..Can any one help me on this issue. As you know that apple is deprecated the UDID method, So there is possibility to generate Unique id using wifi-mac address.But apple is going to remove the wifi mac address in iOS7 version.So my requirement is to generate a new unique code which should work in all iOS versions.Thanks in advance..
Note: Don't change the UDID once user restart the device or reinstall the application.
I was updating my application that was working based only on Unique Identifier which supported iOS 4.3 and above. So,
1) I was unable to use [UIDevice currentDevice].uniqueIdentifier; as it was no longer available
2) I could not use [UIDevice currentDevice].identifierForVendor.UUIDString because it was Available in iOS 6.0 and later only and was unable to use for lower iOS versions.
3) The mac address was not an option as it wasn't allowed in iOS-7
4) OpenUDID was deprecated some time ago and also had issues with iOS-6.
5) Advertisement identifiers were also not available for iOS-5 and below
Finally this was what i did
a) Added SFHFKeychainUtils to the project
b) Generated CFUUID key String
CFUUIDRef cfuuid = CFUUIDCreate(kCFAllocatorDefault);
udidString = (NSString*)CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, cfuuid));
c) Saved it to Key Chain Utils or else it will generate a new Unique Each Time
Final Code
+ (NSString *)GetDeviceID {
NSString *udidString;
udidString = [self objectForKey:#"deviceID"];
if(!udidString)
{
CFUUIDRef cfuuid = CFUUIDCreate(kCFAllocatorDefault);
udidString = (NSString*)CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, cfuuid));
CFRelease(cfuuid);
[self setObject:udidString forKey:#"deviceID"];
}
return udidString;
}
+(void) setObject:(NSString*) object forKey:(NSString*) key
{
NSString *objectString = object;
NSError *error = nil;
[SFHFKeychainUtils storeUsername:key
andPassword:objectString
forServiceName:#"LIB"
updateExisting:YES
error:&error];
if(error)
NSLog(#"%#", [error localizedDescription]);
}
+(NSString*) objectForKey:(NSString*) key
{
NSError *error = nil;
NSString *object = [SFHFKeychainUtils getPasswordForUsername:key
andServiceName:#"LIB"
error:&error];
if(error)
NSLog(#"%#", [error localizedDescription]);
return object;
}
For further Details
Now Device Identifier change to UUID.You can get UUID With the help of following code:
- (NSString *)getUUID
{
NSString *UUID = [[NSUserDefaults standardUserDefaults] objectForKey:#"uniqueID"];
if (!UUID) {
CFUUIDRef theUUID = CFUUIDCreate(NULL);
CFStringRef string = CFUUIDCreateString(NULL, theUUID);
CFRelease(theUUID);
UUID = [(__bridge NSString*)string stringByReplacingOccurrencesOfString:#"-"withString:#""];
[[NSUserDefaults standardUserDefaults] setValue:UUID forKey:#"uniqueID"];
}
return UUID;
}
It's Work in all iOS version.
I don't have access to the code right now (can post in a few hours if you still need it) but what I've done is create a static method 'deviceIdentifier' in a helper class.
the method does a basic check for the current iOS version, returns UDID if below 6.0 and uniqueIdentifier otherwise
Let me know if you'd like the code for that and I'll post it when I can..it's only 10-15 lines or so if I remember right but makes a big difference as then you can just call '[myHelper deviceIdentifier]' wherever you need a device ID and not have to worry about which iOS version they are using
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
UIDevice uniqueIdentifier Deprecated - What To Do Now?
Even if Apple was not at Barcelone's MWC (mobile world congress), there was the certitude that getting the deviceID will be deprecated in further iOS SDK.
I do not understand why Apple want to restrict this, but that's not the topic.
I must prepare my application to an alternative because my users are identified and known for a better use of my app (don't need to log, or create an account, for example). And I'm sure I'm not alone in that case.
So anybody know an alternative from getting the deviceID ? Is there other unique identifier, like MAC address, for example ? How do you prepare your app ?
UPDATE
What about using CFUUID to generate a UUID. You can than store it in KEYCHAIN on the very first launch..
you can get it like this...
NSString *uuid = nil;
CFUUIDRef theUUID = CFUUIDCreate(kCFAllocatorDefault);
if (theUUID) {
uuid = NSMakeCollectable(CFUUIDCreateString(kCFAllocatorDefault, theUUID));
[uuid autorelease];
CFRelease(theUUID);
}
and also by deprecating the uniqueIdentifier method, Apple is suggesting that you don't identify per device but instead per app install. may be tomorrow they might decide to reject your app for doing this .. :/
hoping this helps.
try this
- (NSString *)getDeviceID
{
NSString *uuid = [self gettingString:#"uniqueAppId"];
if(uuid==nil || [uuid isEqualToString:#""])
{
CFUUIDRef theUUID = CFUUIDCreate(kCFAllocatorDefault);
if (theUUID)
{
uuid = NSMakeCollectable(CFUUIDCreateString(kCFAllocatorDefault, theUUID));
[self savingString:#"uniqueAppId" data:uuid];
[uuid autorelease];
CFRelease(theUUID);
}
}
return uuid;
// this is depreciated
// UIDevice *device = [UIDevice currentDevice];
// return [device uniqueIdentifier];
}
Please implement the new logic to get Secure UDID.it is provided by Third Party
Learn about free solution:
This really works fine and is easy to implememt without making it a fuss to replace the deprecated method.
So I'm trying to convert an old project to Automatic Reference Counting. I'm trying to use the conversion tool that xCode has but it says to fix a couple things before it can convert. I have no idea how to fix this error. It's in the implementation of the keychain file. This method is the one that returns the error, specifically the line with the SecItemCopyMatching. The error I am getting says: " Cast of an indirect pointer to an Objective-C pointer to 'CFTypeRef*' (aka 'const void**') is disallowed with ARC. I've been looking all over google, apple docs, and a bunch of other crap and can't find a better way to fetch an existing data dictionary in the keychain. Any help appreciated. Thanks!
-(NSMutableDictionary*)fetchDictionary {
NSMutableDictionary *genericPasswordQuery = [self buildSearchQuery];
NSMutableDictionary *outDictionary = nil;
OSStatus status = SecItemCopyMatching((__bridge_retained CFDictionaryRef)genericPasswordQuery, (CFTypeRef*)&outDictionary);
if (DEBUG) printf("FETCH: %s\n", [[self fetchStatus:status] UTF8String]);
if (status == errSecItemNotFound) return NULL;
return outDictionary;
}
You don't need to disable ARC for this; you just need to declare the query's result as a CFDictionaryRef, then cast it to an NSDictionary after the call.
/*1*/ CFDictionaryRef cfquery = (__bridge_retained CFDictionaryRef)genericPasswordQuery;
/*2*/ CFDictionaryRef cfresult = NULL;
/*3*/ OSStatus status = SecItemCopyMatching(cfquery, (CFTypeRef *)&cfresult);
/*4*/ CFRelease(cfquery);
/*5*/ NSDictionary *result = (__bridge_transfer NSDictionary *)cfresult;
Couple of remarks:
In line 1, we convert the query from Cocoa land to Core Foundation country. We use __bridge_retained to make sure ARC does not release and deallocate the object while we are working with it. This kind of bridging cast retains the object, so to prevent leaks, it must be followed with a corresponding CFRelease somewhere. SecItemCopyMatching definitely will not release the query for us, so if we use a retained bridge, then we need to release the resulting Core Foundation object ourself. (Which we do in line 4.)
Lines 2, 3, and 4 are pure C code using Core Foundation types, so ARC will have nothing to do or complain about them.
In line 5, we tell ARC that SecItemCopyMatching has created its result with a retain count of 1, which we are responsible for releasing. (We know this because it has "Copy" in its name.) __bridge_transfer lets ARC know about this responsibility, so it will be able to do it for us automatically.
Don't cast the immutable Core Foundation dictionary returned by SecItemCopyMatching to NSMutableDictionary; that's just wrong. Also, it is against general Cocoa style conventions that buildSearchQuery returns an NSMutableDictionary. Simple NSDictionarys would work fine in both cases.
The rule of thumb here is that __bridge_retained needs to be followed by a CFRelease, while the result from a "Copy" or "Create" function must be cast into Cocoa-land using __bridge_transfer.
Method 3: Let ARC do the heavy lifting (or a combination of method 1 and method 2):
NSMutableDictionary* query = [NSMutableDictionary dictionaryWithDictionary:
#{
(__bridge id) kSecClass : (__bridge id) kSecClassGenericPassword,
(__bridge id) kSecAttrService : nssService,
#if ! TARGET_IPHONE_SIMULATOR
(__bridge id) kSecAttrAccessGroup : #"PRODUCT.com.COMPANY.GenericKeychainSuite",
#endif
(__bridge id) kSecMatchLimit : (__bridge id) kSecMatchLimitOne,
(__bridge id) kSecReturnAttributes : (__bridge id) kCFBooleanTrue,
}];
if ( [nssAccount length] != 0 )
[query setObject:nssAccount forKey:(__bridge id) kSecAttrAccount];
CFDictionaryRef cfresult;
auto err = ::SecItemCopyMatching((__bridge CFDictionaryRef)query,
(CFTypeRef*)&cfresult);
if ( err == errSecItemNotFound )
return std::wstring();
else if ( err != noErr)
throw std::exception();
NSDictionary* result = (__bridge_transfer NSDictionary*) cfresult;
SecItemCopyMatching doesn't need to own the incoming dictionary, so __bridge is
adequate and then ARC continues to manage the lifetime of query.
And by transferring ownership of the result to arc, it will manage the lifetime
of result as well and free us of the need to remember to CFRelease it on all code
paths.
Method 2: When you use it once, why need a retaining or transfering? example from bottom work at glance for me, testing, debuging(memleaks) all pass :) Only one thing did leak unreleased autoretained variable when key will be found (1)
CFDictionaryRef keyAttributes = NULL; /* variable for store attributes */
NSMutableDictionary *credQuery = [NSMutableDictionary dictionary]; // credential Query
/* Here you add some options for search your key */
OSStatus errGather = SecItemCopyMatching(
(__bridge CFDictionaryRef)credQuery,
(CFTypeRef *)&keyAttributes
);
if (errGather == errSecSuccess) {
// Gather stored key
NSDictionary *keychainDict = (__bridge NSDictionary *)keyAttributes;
NSData *passData = keychainDict[(__bridge id<NSCopying>)kSecValueData]; // password
...
/* work with gathered data :) */
...
CFRelease(keyAttributes); // (1) HERE. Release when CFType is retained really :)
credQuery = nil;
keychainDict = nil;
passData = nil;
}
After trying to print retainCount of object I get 2147483647. Why do I get such a result? It should be 1, shouldn't?
NSString *myStr = [NSString new];
NSUInteger retCount = [myStr retainCount];
NSLog(#"refCount = %u", retCount);
2011-09-08 17:59:18.759 Test[51972:207] refCount = 2147483647
I use XCode Version 4.1. Tried compilers GCC 4.2 and LLVM GCC 4.2 - the same result.
Garbage Collection option was set to unsupported.
NSString is somewhat special when it comes to memory management. String literals (something like #"foo") are effectively singletons, every string with the same content is the same object because it can't be modified anyway. As [NSString new] always creates an empty string that cannot be modified, you'll always get the same instance which cannot be released (thus the high retainCount).
Try this snippet:
NSString *string1 = [NSString new];
NSString *string2 = [NSString new];
NSLog(#"Memory address of string1: %p", string1);
NSLog(#"Memory address of string2: %p", string2);
You'll see that both strings have the same memory address and are therefore the same object.
This doesn't directly answer your question, but retainCount is not really all that useful and should not be used for testing. See this SO post for details.
When to use -retainCount?
While NSString's are an odd case (there are others in the framework) you might also run across this in other clases - it's one of the ways of creating a singleton object.
A singleton only exists once in the app and it's pretty important that it never gets released! Therefore, it will overwrite some methods of NSObject including (but not limited to):
- (id)release {
// Ignore the release!
return self;
}
- (NSUInteger)retainCount {
// We are never going to be released
return UINT_MAX;
}
This object can never be released and tells the framework that it's a singleton by having a ludicrously high retain count.
Checkout this link for more information about singleton objects.
I've seen this a couple of times regarding NSStrings, the retainCount returns the maximum count instead of the actual one when you try to look at retainCounts of strings in this manner.
Try this;
NSString *myStr = [[NSString alloc] init];
NSUInteger retCount = [myStr retainCount];
NSLog(#"refCount = %u", retCount);
Edit: Restored NSUInteger
How to create unique Alphanumeric id from iPhone whenever i required? It must be distinct because it is sharable to all iphone.
I use this code in my Utils class to generate the UUID:
+ (NSString *)newUUID
{
CFUUIDRef uuid = CFUUIDCreate(NULL);
CFStringRef uuidStr = CFUUIDCreateString(NULL, uuid);
NSString *result = [[NSString alloc] initWithFormat:#"%#", [(NSString *)uuidStr lowercaseString]];
CFRelease(uuidStr);
CFRelease(uuid);
return result;
}
The method starts with new, so make sure to release the string when you are done with it, or autorelease it.
Take a look at CFUUID. A UUID can be expected to be unique across space and time (give or take a few caveats.)