原文鏈接:http://blog.sina.com.cn/s/blog_5971cdd00102vqgy.html
(絕對有用)iOS獲取UUID,並使用keychain存儲 此博文包含圖片 (2015-05-18 17:32:14)轉載▼ 標簽: ios it 森女 uuid keychain 分類: iOS技術 UDID被棄用,使用UUID來作為設備的唯一標識。獲取到UUID后,如果用NSUserDefaults存儲,當程序被卸載后重裝時,再獲得的UUID和之前就不同了。使用keychain存儲可以保證程序卸載重裝時,UUID不變。但當刷機或者升級系統后,UUID還是會改變的。但這仍是目前為止最佳的解決辦法了,如果有更好的解決辦法,歡迎留言。 (我整理的解決辦法的參考來源:http://blog.k-res.net/archives/1081.html) 首先對於之前寫的文章自己沒有用真機測試,以及這段時間大家加我的QQ或者私信我也好,有的甚至找到了我的旺旺上,但是我沒有給大家及時回復很抱歉。 進入正題,我之后又試了下自己寫的方法,發現用模擬器可以,但是真機不可以。 真機卸載后和之前獲取的uuid不同,究其原因就是之前生成的uuid並沒有被keychain成功存儲。 所以問題在keychain.我之前給大家的KeychainItemWrapper類是蘋果官方的,但是有些朋友反映運行時會崩潰。 除此之外,Code Signing Entitlements的創建方法也不夠嚴謹。 所以,請大家重新跟我走一遍吧。 1.新建一個工程,看一下自己的Bundle Id.這個Bundle Id 要和你用真機測試時的證書上面的Bundle Id相匹配。 (絕對有用)iOS獲取UUID,並使用keychain存儲 比如我的是 house.xianrou.xianrou 2.Target - Capabilities - Keychain Sharing - ON (絕對有用)iOS獲取UUID,並使用keychain存儲 (絕對有用)iOS獲取UUID,並使用keychain存儲 這步主要目的是打開Keychain Sharing,將它由灰色狀態的OFF改為藍色狀態的ON。 打開之后的變化如下: (絕對有用)iOS獲取UUID,並使用keychain存儲 (絕對有用)iOS獲取UUID,並使用keychain存儲 左側的目錄會自動生成Entitlements文件,不需要自己創建了。 也就是說,Bundle Identifier、Keychain Sharing的Keychain Groups、Entitlements文件的Keychain Access Groups的第一個元素,它們要保持上圖所示的一致性。 設置好了以后可以運行下程序,沒問題可以進行下一步。 3.傳說中的uuid類和keychain類來啦 既然蘋果的keychain方法會崩潰而且有些復雜,我們只保存一個uuid的話可以用下面的簡單方法: (這也是我自己百度的keychain拷貝別人的,然后改改) UUID.h #import 尖括號(Foundation/Foundation.h) @interface UUID : NSObject +(NSString *)getUUID; @end UUID.m #import "UUID.h" #import "KeyChainStore.h" @implementation UUID +(NSString *)getUUID { NSString * strUUID = (NSString *)[KeyChainStore load:@"com.company.app.usernamepassword"]; //首次執行該方法時,uuid為空 if ([strUUID isEqualToString:@""] || !strUUID) { //生成一個uuid的方法 CFUUIDRef uuidRef = CFUUIDCreate(kCFAllocatorDefault); strUUID = (NSString *)CFBridgingRelease(CFUUIDCreateString (kCFAllocatorDefault,uuidRef)); //將該uuid保存到keychain [KeyChainStore save:KEY_USERNAME_PASSWORD data:strUUID]; } return strUUID; } @end KeyChainStore.h #import 尖括號(Foundation/Foundation.h) @interface KeyChainStore : NSObject + (void)save:(NSString *)service data:(id)data; + (id)load:(NSString *)service; + (void)deleteKeyData:(NSString *)service; @end KeyChainStore.m #import "KeyChainStore.h" @implementation KeyChainStore + (NSMutableDictionary *)getKeychainQuery:(NSString *)service { return [NSMutableDictionary dictionaryWithObjectsAndKeys: (id)kSecClassGenericPassword,(id)kSecClass, service, (id)kSecAttrService, service, (id)kSecAttrAccount, (id)kSecAttrAccessibleAfterFirstUnlock,(id)kSecAttrAccessible, nil]; } + (void)save:(NSString *)service data:(id)data { //Get search dictionary NSMutableDictionary *keychainQuery = [self getKeychainQuery:service]; //Delete old item before add new item SecItemDelete((CFDictionaryRef)keychainQuery); //Add new object to search dictionary(Attention:the data format) [keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(id)kSecValueData]; //Add item to keychain with the search dictionary SecItemAdd((CFDictionaryRef)keychainQuery, NULL); } + (id)load:(NSString *)service { id ret = nil; NSMutableDictionary *keychainQuery = [self getKeychainQuery:service]; //Configure the search setting //Since in our simple case we are expecting only a single attribute to be returned (the password) we can set the attribute kSecReturnData to kCFBooleanTrue [keychainQuery setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData]; [keychainQuery setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit]; CFDataRef keyData = NULL; if (SecItemCopyMatching((CFDictionaryRef)keychainQuery, (CFTypeRef *)&keyData) == noErr) { @try { ret = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge NSData *)keyData]; } @catch (NSException *e) { NSLog(@"Unarchive of %@ failed: %@", service, e); } @finally { } } if (keyData) CFRelease(keyData); return ret; } + (void)deleteKeyData:(NSString *)service { NSMutableDictionary *keychainQuery = [self getKeychainQuery:service]; SecItemDelete((CFDictionaryRef)keychainQuery); } @end 將這兩個類添加到工程中 4.新建一個pch文件,然后pch文件的內容如下: #ifndef PrefixHeader_pch #define PrefixHeader_pch #define KEY_USERNAME_PASSWORD @"com.company.app.usernamepassword" #define KEY_USERNAME @"com.company.app.username" #define KEY_PASSWORD @"com.company.app.password" #endif pch文件的創建方法可參考:http://blog.csdn.net/huang2009303513/article/details/40375235 你有可能會在填Prefix Header 即pch文件的路徑那里報錯,最近又學習到一種更好的方式$(SRCROOT)/$(PROJECT_NAME)/PrefixHeader.pch,其中$(PROJECT_NAME)是相對工程名,比上面的方法更便捷. 5.在viewcontroller.m里面執行如下代碼 NSString * uuid= [UUID getUUID]; NSLog(@"uuid=%@",uuid); 得到的uuid類似於這種 uuid=19AAB430-9CB8-4325-ACC5-D7D386B68960 然后卸載掉,再重新運行,看前后得到的uuid是不是一樣吧!