什么是UUID? UUID「Universally Unique Identifier」通用唯一標(biāo)識符配深,是一個32位的十六進制序列,使用小橫線來連接:8-4-4-4-12。
在開發(fā)過程中妹懒,我們通常需要回去手機的唯一標(biāo)識符來區(qū)別不用的用戶,或者說需要將用戶的賬號與手機綁定双吆。如我們的項目需要一個賬號只能給一個人使用眨唬,所以我選擇使用賬號綁定手機,來盡量避免無法追蹤數(shù)據(jù)庫操作的操作人好乐,那么我們就需要獲取到手機的唯一標(biāo)識符在每次登陸賬號的時候傳給后臺作為登陸的限定條件之一匾竿。
??在 iOS 5 中, 可以獲取到系統(tǒng)的 UDID(Unique Device Identifier)
蔚万,后來被 Apple 禁止了岭妖。
??在iOS 6 中,大家開始使用MAC地址 (Medium/Media Access Control)
,后來又被Apple禁止了。
??UDID和MAC地址被棄用昵慌,使用UUID來作為設(shè)備的唯一標(biāo)識苔巨。獲取到UUID后,如果NSUserDefaults存儲废离,當(dāng)程序被卸載后重裝時侄泽,再獲得的UUID和之前就不同了。使用keychain存儲可以保證程序卸載重裝時蜻韭,UUID不變悼尾。但當(dāng)刷機或者升級系統(tǒng)后,UUID還是會改變的肖方。
??那么讓我們來進行 UUID + keychain 獲取唯一標(biāo)識符的步驟:
1. 新建一個工程闺魏,看一下自己的Bundle Id.這個Bundle Id 要和你用真機測試時的證書上面的Bundle Id相匹配。
2. Target - Capabilities - Keychain Sharing - ON
這步主要目的是打開Keychain Sharing,將它由灰色狀態(tài)的OFF改為藍色狀態(tài)的ON俯画。
打開之后的變化如下:
左側(cè)的目錄會自動生成Entitlements文件析桥,不需要自己創(chuàng)建了。也就是說艰垂,Bundle Identifier泡仗、Keychain Sharing的Keychain Groups、Entitlements文件的Keychain Access Groups的第一個元素猜憎,它們要保持上圖所示的一致性娩怎。
設(shè)置好了以后可以運行下程序,沒問題可以進行下一步胰柑。
3. 傳說中的uuid類和keychain類來啦
既然蘋果的keychain方法會崩潰而且有些復(fù)雜截亦,我們只保存一個UUID的話可以用下面的簡單方法:
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"];
//首次執(zhí)行該方法時,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
將這兩個類添加到工程中,使用方式如下
NSString * uuid= [UUID getUUID]
將程序?qū)懺谥笾匦掳惭b柬讨,我們依然能獲取同樣的UUID崩瓤。需要注意的是當(dāng)手機恢復(fù)出廠設(shè)置后獲取的UUID是不一樣的了。