iOS唯一標識 的最優解決方案
iOS裝置UUID的獲取方法(不變的唯一值)
UDID被棄用,使用UUID來作為裝置的唯一標識。獲取到UUID後,如果用NSUserDefaults儲存,當程式被解除安裝後重裝時,再獲得的UUID和之前就不同了。使用keychain儲存可以保證程式解除安裝重灌時,UUID不變。但當刷機或者升級系統後,UUID還是會改變的。但這仍是目前為止最佳的解決辦法了,如果有更好的解決辦法,歡迎留言。
進入正題,我之後又試了下自己寫的方法,發現用模擬器可以,但是真機不可以。
真機解除安裝後和之前獲取的uuid不同,究其原因就是之前生成的uuid並沒有被keychain成功儲存。
所以問題在keychain.我之前給大家的KeychainItemWrapper類是蘋果官方的,但是有些朋友反映執行時會崩潰。
除此之外,Code SigningEntitlements的建立方法也不夠嚴謹。
1.新建一個工程,看一下自己的BundleId.這個BundleId 要和你用真機測試時的證書上面的Bundle Id相匹配。
2.Target - Capabilities - KeychainSharing - ON
這步主要目的是開啟KeychainSharing,將它由灰色狀態的OFF改為藍色狀態的ON。
開啟之後的變化如下:
左側的目錄會自動生成Entitlements檔案,不需要自己建立了。
也就是說,BundleIdentifier、Keychain Sharing的Keychain Groups、Entitlements檔案的KeychainAccess 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*)[KeyChainStoreload:@”com.company.app.usernamepassword”];
//首次執行該方法時,uuid為空
if([strUUIDisEqualToString:@”“]|| !strUUID)
{
//生成一個uuid的方法
CFUUIDRef uuidRef= CFUUIDCreate(kCFAllocatorDefault);
strUUID = (NSString*)CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault,uuidRef));
//將該uuid儲存到keychain
[KeyChainStoresave:KEY_USERNAME_PASSWORDdata:strUUID];
}
returnstrUUID;
}
@end
KeyChainStore.h
import尖括號(Foundation/Foundation.h)
@interfaceKeyChainStore :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[NSMutableDictionarydictionaryWithObjectsAndKeys:
(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 = [selfgetKeychainQuery:service];
//Delete old item before add new item
SecItemDelete((CFDictionaryRef)keychainQuery);
//Add new object to searchdictionary(Attention:the data format)
[keychainQuery setObject:[NSKeyedArchiverarchivedDataWithRootObject:data]forKey:(id)kSecValueData];
//Add item to keychain with the searchdictionary
SecItemAdd((CFDictionaryRef)keychainQuery,NULL);
}(id)load:(NSString*)service {
idret =nil;
NSMutableDictionary*keychainQuery = [selfgetKeychainQuery:service];
//Configure the search setting
//Since in our simple case we areexpecting only a single attribute to be returned (the password) wecan set the attribute kSecReturnData to kCFBooleanTrue
[keychainQuerysetObject:(id)kCFBooleanTrueforKey:(id)kSecReturnData];
[keychainQuery setObject:(id)kSecMatchLimitOneforKey:(id)kSecMatchLimit];
CFDataRefkeyData =NULL;
if(SecItemCopyMatching((CFDictionaryRef)keychainQuery,(CFTypeRef*)&keyData) ==noErr){
@try{
ret =[NSKeyedUnarchiverunarchiveObjectWithData:(__bridgeNSData*)keyData];
}@catch(NSException *e) {
NSLog(@”Unarchiveof %@ failed: %@”,service, e);
}@finally{
}
}
if(keyData)
CFRelease(keyData);
returnret;
}(void)deleteKeyData:(NSString*)service {
NSMutableDictionary*keychainQuery = [selfgetKeychainQuery:service];
SecItemDelete((CFDictionaryRef)keychainQuery);
}
@end
將這兩個類新增到工程中
4.新建一個pch檔案,然後pch檔案的內容如下:
ifndef PrefixHeader_pch
define PrefixHeader_pch
define [email protected]”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檔案的路徑那裡報錯,最近又學習到一種更好的方式
(PROJECT_NAME)/PrefixHeader.pch,其中$(PROJECT_NAME)是相對工程名,比上面的方法更便捷.
5.在viewcontroller.m裡面執行如下程式碼
NSString * uuid= [UUIDgetUUID];
NSLog(@”uuid=%@”,uuid);
得到的uuid類似於這種
uuid=19AAB430-9CB8-4325-ACC5-D7D386B68960
然後解除安裝掉,再重新執行,看前後得到的uuid是不是一樣吧!