1. 程式人生 > >iOS唯一標識 的最優解決方案

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檔案的路徑那裡報錯,最近又學習到一種更好的方式 ( S R C R O O T ) / (PROJECT_NAME)/PrefixHeader.pch,其中$(PROJECT_NAME)是相對工程名,比上面的方法更便捷.

5.在viewcontroller.m裡面執行如下程式碼

NSString * uuid= [UUIDgetUUID];
NSLog(@”uuid=%@”,uuid);
得到的uuid類似於這種
uuid=19AAB430-9CB8-4325-ACC5-D7D386B68960

然後解除安裝掉,再重新執行,看前後得到的uuid是不是一樣吧!