(绝对有用)iOS获取UUID,并使用keychain存储
?
UDID被棄用,使用UUID來作為設備的唯一標識。獲取到UUID后,如果用NSUserDefaults存儲,當程序被卸載后重裝時,再獲得的UUID和之前就不同了。使用keychain存儲可以保證程序卸載重裝時,UUID不變。但當刷機或者升級系統后,UUID還是會改變的。但這仍是目前為止最佳的解決辦法了,如果有更好的解決辦法,歡迎留言。
(我整理的解決辦法的參考來源:http://blog.k-res.net/archives/1081.html) 給大家兩個類: UUID.h中的代碼:#import 尖括號(Foundation/Foundation.h)
@interface UUID : NSObject
+(NSString *)getUUID;
@end
?
UUID.m中的代碼:#import "UUID.h"
#import?尖括號(Foundation/Foundation.h)
#import "KeychainItemWrapper.h"
@implementation UUID
+(NSString *)getUUID
{
? ? KeychainItemWrapper *keychainItem = [[KeychainItemWrapper alloc]
?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?initWithIdentifier:@"UUID"
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?accessGroup:@"YOUR_BUNDLE_SEED.com.yourcompany.userinfo"];
? ?
? ?NSString *strUUID = [keychainItem objectForKey:(id)CFBridgingRelease(kSecValueData)];
? ? //首次執行該方法時,uuid為空
? ?if ([strUUID isEqualToString:@""])
? ? {
? ? ? ? CFUUIDRef uuidRef = CFUUIDCreate(kCFAllocatorDefault);
? ? ? ? strUUID = (NSString *)CFBridgingRelease(CFUUIDCreateString (kCFAllocatorDefault,uuidRef));
? ? ? ? [keychainItem setObject:strUUID forKey:(id)CFBridgingRelease(kSecValueData)];
??
? ? }
? ?return strUUID;
}
@end
第二個類是蘋果官方的一個Demo里封裝的,Demo的下載地址:http://developer.apple.com/library/ios/#samplecode/GenericKeychain/Listings/Classes_KeychainItemWrapper_h.html#//apple_ref/doc/uid/DTS40007797-Classes_KeychainItemWrapper_h-DontLinkElementID_9
KeychainItemWrapper.h中的代碼:
#import 尖括號(UIKit/UIKit.h)
@interface KeychainItemWrapper : NSObject
{
? ? NSMutableDictionary *keychainItemData;
? ? NSMutableDictionary *genericPasswordQuery;
}
@property (nonatomic, retain) NSMutableDictionary *keychainItemData;
@property (nonatomic, retain) NSMutableDictionary *genericPasswordQuery;
- (id)initWithIdentifier: (NSString *)identifier accessGroup:(NSString *) accessGroup;
- (void)setObject:(id)inObject forKey:(id)key;
- (id)objectForKey:(id)key;
- (void)resetKeychainItem;
@end
KeychainItemWrapper.m中的代碼:
#import "KeychainItemWrapper.h"
#import 尖括號(Security/Security.h)
@interface KeychainItemWrapper (PrivateMethods)
- (NSMutableDictionary *)secItemFormatToDictionary:(NSDictionary *)dictionaryToConvert;
- (NSMutableDictionary *)dictionaryToSecItemFormat:(NSDictionary *)dictionaryToConvert;
- (void)writeToKeychain;
@end
@implementation KeychainItemWrapper
@synthesize keychainItemData, genericPasswordQuery;
- (id)initWithIdentifier: (NSString *)identifier accessGroup:(NSString *) accessGroup;
{
? ?if (self = [super init])
? ? {
? ? ? ? ?genericPasswordQuery = [[NSMutableDictionary alloc] init];
?? ? ? ?
[genericPasswordQuery setObject:(id)CFBridgingRelease(kSecClassGenericPassword) forKey:(id)kSecClass];
? ? ? ? [genericPasswordQuery setObject:identifier forKey:(id)CFBridgingRelease(kSecAttrGeneric)];
?
if (accessGroup != nil)
{
#if TARGET_IPHONE_SIMULATOR
?
#else
[genericPasswordQuery setObject:accessGroup forKey:(id)kSecAttrAccessGroup];
#endif
}
?
?
? ? ? ? [genericPasswordQuery setObject:(id)CFBridgingRelease(kSecMatchLimitOne) forKey:(id)kSecMatchLimit];
? ? ? ? [genericPasswordQuery setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnAttributes];
?? ? ? ?
? ? ? ?NSDictionary *tempQuery = [NSDictionary dictionaryWithDictionary:genericPasswordQuery];
?? ? ? ?
? ? ? ?NSMutableDictionary *outDictionary = nil;
?? ? ? ?
? ? ? ?if (! SecItemCopyMatching((CFDictionaryRef)tempQuery, (CFTypeRef *)&outDictionary) == noErr)
? ? ? ? {
? ? ? ? ? ??
? ? ? ? ? ? [self resetKeychainItem];
?
[keychainItemData setObject:identifier forKey:(id)kSecAttrGeneric];
if (accessGroup != nil)
{
#if TARGET_IPHONE_SIMULATOR
?
#else
[keychainItemData setObject:accessGroup forKey:(id)kSecAttrAccessGroup];
#endif
}
}
? ? ? ?else
? ? ? ? {
? ? ? ? ? ?self.keychainItemData = [self secItemFormatToDictionary:outDictionary];
? ? ? ? }
? ? ? ?
[outDictionary release];
? ? }
?? ?
return self;
}
- (void)dealloc
{
? ? [keychainItemData release];
? ? [genericPasswordQuery release];
?? ?
[super dealloc];
}
- (void)setObject:(id)inObject forKey:(id)key?
{
? ?if (inObject == nil) return;
? ?id currentObject = [keychainItemData objectForKey:key];
? ?if (![currentObject isEqual:inObject])
? ? {
? ? ? ? [keychainItemData setObject:inObject forKey:key];
? ? ? ? [self writeToKeychain];
? ? }
}
- (id)objectForKey:(id)key
{
? ? return [keychainItemData objectForKey:key];
}
- (void)resetKeychainItem
{
OSStatus junk = noErr;
? ? if (!keychainItemData)?
? ? {
? ? ? ?self.keychainItemData = [[NSMutableDictionary alloc] init];
? ? }
? ? else if (keychainItemData)
? ? {
? ? ? ?NSMutableDictionary *tempDictionary = [self dictionaryToSecItemFormat:keychainItemData];
junk = SecItemDelete((CFDictionaryRef)tempDictionary);
? ? ? ? NSAssert( junk == noErr || junk == errSecItemNotFound, @"Problem deleting current dictionary." );
? ? }
?? ?
? ? [keychainItemData setObject:@"" forKey:(id)kSecAttrAccount];
? ? [keychainItemData setObject:@"" forKey:(id)kSecAttrLabel];
? ? [keychainItemData setObject:@"" forKey:(id)kSecAttrDescription];?
? [keychainItemData setObject:@"" forKey:(id)kSecValueData];
}
- (NSMutableDictionary *)dictionaryToSecItemFormat:(NSDictionary *)dictionaryToConvert
{
? ?NSMutableDictionary *returnDictionary = [NSMutableDictionary dictionaryWithDictionary:dictionaryToConvert];
? ? [returnDictionary setObject:(id)kSecClassGenericPasswordforKey:(id)kSecClass];
??NSString *passwordString = [dictionaryToConvert objectForKey:(id)kSecValueData];
? ? [returnDictionary setObject:[passwordString dataUsingEncoding:NSUTF8StringEncoding] forKey:(id)kSecValueData];
?? ?
? ?return returnDictionary;
}
- (NSMutableDictionary *)secItemFormatToDictionary:(NSDictionary *)dictionaryToConvert
{
? ?NSMutableDictionary *returnDictionary = [NSMutableDictionary dictionaryWithDictionary:dictionaryToConvert];
? ??[returnDictionary setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
? ? ? ? ?[returnDictionary setObject:(id)kSecClassGenericPasswordforKey:(id)kSecClass];
? ? ?NSData *passwordData = NULL;
? ?if (SecItemCopyMatching((CFDictionaryRef)returnDictionary, (CFTypeRef *)&passwordData) == noErr)
? ? {
? ? ? ??[returnDictionary removeObjectForKey:(id)kSecReturnData];
? ? ? ??NSString *password = [[[NSString alloc] initWithBytes:[passwordData bytes] length:[passwordData length]?
?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?encoding:NSUTF8StringEncoding] autorelease];
? ? ? ? [returnDictionary setObject:password forKey:(id)kSecValueData];
? ? }
? ?else
? ? {
? ? ? ? ? ?NSAssert(NO, @"Serious error, no matching item found in the keychain.\n");
? ? }
?? ?
? ? [passwordData release];
? ?
return returnDictionary;
}
- (void)writeToKeychain
{
? ?NSDictionary *attributes = NULL;
? ?NSMutableDictionary *updateItem = NULL;
OSStatus result;
?? ?
? ?if (SecItemCopyMatching((CFDictionaryRef)genericPasswordQuery, (CFTypeRef *)&attributes) == noErr)
? ? {
? ? ? ??updateItem = [NSMutableDictionary dictionaryWithDictionary:attributes];
? ? ? ? [updateItem setObject:[genericPasswordQuery objectForKey:(id)kSecClass] forKey:(id)kSecClass];
? ? ? ?NSMutableDictionary *tempCheck = [self dictionaryToSecItemFormat:keychainItemData];
? ? ? ? [tempCheck removeObjectForKey:(id)kSecClass];
?
#if TARGET_IPHONE_SIMULATOR
?
[tempCheck removeObjectForKey:(id)kSecAttrAccessGroup];
#endif
? ? ? ? result = SecItemUpdate((CFDictionaryRef)updateItem, (CFDictionaryRef)tempCheck);
NSAssert( result == noErr, @"Couldn't update the Keychain Item." );
? ? }
? ?else
? ? {
? ? ? ??result = SecItemAdd((CFDictionaryRef)[self dictionaryToSecItemFormat:keychainItemData], NULL);
NSAssert( result == noErr, @"Couldn't add the Keychain Item." );
? ? }
}
?
@end
PS: 1.?KeychainItemWrapper類在官方Demo里面也有,如果我復制出錯了,大家可以在上面的鏈接上下載官方Demo. ? ?2.使用時要添加Security.framework ? ?3.?尤為注意的是,UUID類下面這句代碼中group的設置方法。 ? ? KeychainItemWrapper *keychainItem = [[KeychainItemWrapper alloc]?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?initWithIdentifier:@"UUID"
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?accessGroup:@"YOUR_BUNDLE_SEED.com.yourcompany.userinfo"];
? ? (1)在項目相同的目錄下創建KeychainAccessGroups.plist文件。
? ? ? ? ?
該文件的結構中最頂層的節點必須是一個名為“keychain-access-groups”的Array,并且該Array中每一項都是一個描述分組的NSString。YOUR_BUNDLE_SEED.com.yourcompany.userinfo就是要設置的組名。 ? ? ? ?(2)在項目相同的目錄下創建KeychainAccessGroups.plist文件。在Target-Build Settings-Code Signing欄下的Code Signing?Entitlements右側添加KeychainAccessGroups.plist,如下圖。 ? ? ? ? ??
到此,工作就完成了。 首次安裝程序時,打印出一個uuid,當把程序卸載后,再用getUUID獲得 uuid,打印出來的結果和之前相同。證明達到目的。 測試代碼:
??NSString * uuid= [UUID getUUID];
?NSLog(@"uuid=%@",uuid);
? 測試結果: ??uuid=19AAB430-9CB8-4325-ACC5-D7D386B68960
轉載于:https://www.cnblogs.com/hxwj/p/4828032.html
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的(绝对有用)iOS获取UUID,并使用keychain存储的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: I am BACKKKKKK
- 下一篇: 使用Powershell批量获取Exch