首先,簡(jiǎn)單介紹一下UDID這個(gè)東西:
UDID是Unique Device Identifier的簡(jiǎn)稱,也就是唯一設(shè)備標(biāo)識(shí)的意思。于iOS SDK中取得的方法是UIDevice的一個(gè)叫uniqueIdentifier的NSString*,由于這個(gè)ID字符串是基于設(shè)備的,應(yīng)用開發(fā)人員可以通過(guò)獲取此ID來(lái)用于記錄區(qū)分設(shè)備。正是由于這個(gè)特性,可能會(huì)導(dǎo)致一些隱私等等相關(guān)的問(wèn)題,Apple于iOS5中將這個(gè)UDID廢掉了,SDK中被標(biāo)記為了Deprecated,雖然為了兼容低版本的源代碼而繼續(xù)存在,但并不會(huì)再返回任何有實(shí)際意義的東西。
最近在做Flurry的統(tǒng)計(jì)功能時(shí),發(fā)現(xiàn)還是需要用到可以識(shí)別設(shè)備的東西的,好方便分析數(shù)據(jù),經(jīng)過(guò)一段時(shí)間的研究、試驗(yàn),發(fā)現(xiàn)了這個(gè)應(yīng)該還算是比較靠譜的方法……
其實(shí)早在UDID被deprecated的消息剛出來(lái)時(shí),就已經(jīng)有很多人開始研究對(duì)策了,我也google到了各種五花八門的解決方案,最后還是覺(jué)得這個(gè)UUID的方案比較合適,畢竟是蘋果官方文檔里推薦的替換UDID的方法。
關(guān)于UUID的具體說(shuō)明可以查看下面參考文章中給出的蘋果官方文檔鏈接。簡(jiǎn)單來(lái)說(shuō),UUID就是一個(gè)隨機(jī)序列字符串生成器,有點(diǎn)像Microsoft Windows的COM GUID生成器的作用,比起自己隨機(jī)一個(gè)字符串的好處就是這東西能夠保證唯一性,適用于標(biāo)記。調(diào)用方法如下:
1 | CFUUIDRef uuidRef = CFUUIDCreate(kCFAllocatorDefault); |
2 | NSString * uuid = (NSString *)CFUUIDCreateString (kCFAllocatorDefault,uuidRef); |
然后呢,官方建議的做法是用:
1 | NSUserDefaults *userDefaults = [ NSUserDefaults standardUserDefaults]; |
2 | [userDefaults setObject: uuid forKey:@"UUID"]; |
這樣的做法把生成的ID保存起來(lái),下次再用的時(shí)候就直接讀取已經(jīng)保存的ID了。顯然,那個(gè)UUID生成只是個(gè)“隨機(jī)字符串”生成器,并不能像UDID那樣保證每次取得的串都一樣!保存起來(lái)雖然能保證用戶再次打開這個(gè)應(yīng)用時(shí),能夠獲得一致的標(biāo)識(shí)ID,但不能保證用戶刪除應(yīng)用重新安裝后這個(gè)ID的一致性,因?yàn)镹SUserDefaults只是個(gè)像游戲存檔一樣的東西,游戲刪了,存檔也就跟著一塊刪了。所以,這個(gè)“存存檔”的方法并不是一個(gè)比較完善的解決方案,一個(gè)更好的做法是利用keychain保存這個(gè)生成的UUID。
關(guān)于keychain這個(gè)東西的概念可以到這里學(xué)習(xí):https://developer.apple.com/library/ios/#documentation/Security/Conceptual/keychainServConcepts/01introduction/introduction.html,簡(jiǎn)言之就是每個(gè)應(yīng)用程序都有一個(gè)可以用于安全保存一些如密碼、認(rèn)證等信息的keychain,通過(guò)對(duì)應(yīng)用簽名時(shí)的一些設(shè)置,還可以利用keychain的方式實(shí)現(xiàn)同一開發(fā)者簽證(就是相同bundle seed)下的不同應(yīng)用之間共享信息的操作。比如你有一個(gè)開發(fā)者帳戶,并開發(fā)了兩個(gè)不同的應(yīng)用A和B,然后通過(guò)對(duì)A和B的keychain access group這個(gè)東西指定共用的訪問(wèn)分組,就可以實(shí)現(xiàn)共享此keychain中的內(nèi)容。而且,對(duì)比NSUserDefaults的一點(diǎn)不同之處就是此信息不會(huì)隨應(yīng)用的刪除而消失!
關(guān)于Keychain的應(yīng)用,Apple提供了一個(gè)叫GenericKeychain的例子程序,在這里:http://developer.apple.com/library/ios/#samplecode/GenericKeychain/Listings/Classes_KeychainItemWrapper_h.html#//apple_ref/doc/uid/DTS40007797-Classes_KeychainItemWrapper_h-DontLinkElementID_9,其中封裝了一個(gè)簡(jiǎn)化Keychain操作的類:KeychainItemWrapper,可以拿來(lái)直接使用,記得加入Security.framework!
參考代碼如下:
1 | KeychainItemWrapper *keychainItem = [[KeychainItemWrapper alloc] |
2 | initWithIdentifier: @"UUID" |
3 | accessGroup: @"YOUR_BUNDLE_SEED.com.yourcompany.userinfo" ]; |
4 | NSString *strUUID = [keychainItem objectForKey:( id )kSecValueData]; |
5 | if ([strUUID isEqualToString: @"" ]) |
6 | { |
7 | CFUUIDRef uuidRef = CFUUIDCreate(kCFAllocatorDefault); |
8 | strUUID = ( NSString *)CFUUIDCreateString (kCFAllocatorDefault,uuidRef); |
9 | [keychainItem setObject:strUUID forKey:( id )kSecValueData]; |
10 | } |
11 | [FlurryAnalytics setUserID:strUUID]; |
12 | [keychainItem release]; |
關(guān)于keychain access groups的設(shè)置,傳統(tǒng)方法是在Xcode項(xiàng)目target的Build Settings的Code Signing段中加入Code Signing Entitlements的配置文件,加入group信息,詳細(xì)操作搜索一下就能找到。新版本的Xcode直接整合了生成Entitlements的功能,在指定target的Summary的最后一個(gè)段Entitlements中勾選Enable Entitlements,然后在下面的Keychain Access Groups中加入”com.yourcompany.userinfo”類的共享group名。這里要注意一點(diǎn),參考IDE默認(rèn)生成的對(duì)應(yīng)app自身id名的group可以發(fā)現(xiàn),這里的group并沒(méi)有加入bundle seed,看一下生成的entitlements文件中的內(nèi)容可以發(fā)現(xiàn)group名頭部被自動(dòng)添加了“$(AppIdentifierPrefix)”這種替換變量!
在最開始測(cè)試Keychain讀寫時(shí),參考下面”如何解決蘋果公司禁止用UUID的方法”這篇文章中的方法,一直在keychainItem setObject時(shí)報(bào)異常,斷點(diǎn)跟蹤了一下,發(fā)現(xiàn)是封裝類的writeToKeychain函數(shù)中的SecItemAdd函數(shù)返回
errSecParam = -50, /* One or more parameters passed to a function where not valid. */
這個(gè)錯(cuò)誤,最初以為是access group設(shè)置的問(wèn)題,替換參數(shù)為nil后報(bào)錯(cuò)依舊.最后google到stack overflow上的”Storing keys in KeyChain with KeyChainItemWrapper”這篇問(wèn)答后意識(shí)到原來(lái)setObject的key不能隨意指定任意的string,而必須使用預(yù)定義好的一些key,替換key為kSecValueData后,問(wèn)題解決。
參考文章:
Deprecated UIDevice Methods https://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIDevice_Class/DeprecationAppendix/AppendixADeprecatedAPI.html#//apple_ref/occ/instp/UIDevice/uniqueIdentifier
如何解決蘋果公司禁止用UUID的方法。 http://blog.csdn.net/mengtnt/article/details/7410373
Storing keys in KeyChain with KeyChainItemWrapper http://stackoverflow.com/questions/7117885/storing-keys-in-keychain-with-keychainitemwrapper
聯(lián)系客服