我個人認為 (UUID + keychain) 的解決方法是目前最好的解決方法躺苦。
下面我列舉幾個方法做對比。
各種獲取設(shè)備唯一標(biāo)識的方法介紹
一.UDID(Unique Device Identifier)
UDID的全稱是Unique Device Identifier,它就是蘋果iOS設(shè)備的唯一識別碼桥氏,它由40位16進制數(shù)的字母和數(shù)字組成(越獄的設(shè)備通過某些工具可以改變設(shè)備的UDID)。移動網(wǎng)絡(luò)可利用UDID來識別移動設(shè)備,但是裸卫,從IOS5.0(2011年8月份)開始,蘋果宣布將不再支持用uniqueIdentifier方法獲取設(shè)備的UDID纽竣,iOS5以下是可以用的墓贿。蘋果從iOS5開始就移除了通過代碼訪問UDID的權(quán)限。從2013年5月1日起蜓氨,試圖訪問UIDIDs的程序?qū)⒉辉俦粚徍送ㄟ^聋袋,替代的方案是開發(fā)者應(yīng)該使用“在iOS 6中介紹的Vendor或Advertising標(biāo)示符”。所以UDID是絕對是不能再使用了穴吹。
//UUID , 已廢除NSString*udid = [[UIDevice currentDevice] uniqueIdentifier];
1
2
為什么蘋果反對開發(fā)人員使用UDID幽勒???
iOS 2.0版本以后UIDevice提供一個獲取設(shè)備唯一標(biāo)識符的方法uniqueIdentifier,通過該方法我們可以獲取設(shè)備的序列號港令,這個也是目前為止唯一可以確認唯一的標(biāo)示符啥容。 許多開發(fā)者把UDID跟用戶的真實姓名锈颗、密碼、住址咪惠、其它數(shù)據(jù)關(guān)聯(lián)起來击吱;網(wǎng)絡(luò)窺探者會從多個應(yīng)用收集這些數(shù)據(jù),然后順藤摸瓜得到這個人的許多隱私數(shù)據(jù)遥昧。同時大部分應(yīng)用確實在頻繁傳輸UDID和私人信息覆醇。 為了避免集體訴訟,蘋果最終決定在iOS 5 的時候炭臭,將這一慣例廢除永脓,開發(fā)者被引導(dǎo)生成一個唯一的標(biāo)識符,只能檢測應(yīng)用程序鞋仍,其他的信息不提供〕4荩現(xiàn)在應(yīng)用試圖獲取UDID已被禁止且不允許上架。
二.UUID(Universally Unique Identifier)
UUID是Universally Unique Identifier的縮寫威创,中文意思是通用唯一識別碼排宰。它是讓分布式系統(tǒng)中的所有元素,都能有唯一的辨識資訊那婉,而不需要透過中央控制端來做辨識資訊的指定板甘。這樣,每個人都可以建立不與其它人沖突的 UUID详炬。在此情況下盐类,就不需考慮數(shù)據(jù)庫建立時的名稱重復(fù)問題。蘋果公司建議使用UUID為應(yīng)用生成唯一標(biāo)識字符串呛谜。?
獲得的UUID值系統(tǒng)沒有存儲, 而且每次調(diào)用得到UUID在跳,系統(tǒng)都會返回一個新的唯一標(biāo)示符。如果你希望存儲這個標(biāo)示符隐岛,那么需要自己將其存儲到NSUserDefaults, Keychain, Pasteboard或其它地方猫妙。
從iOS2.0開始,CFUUID就已經(jīng)出現(xiàn)了聚凹。它是CoreFoundatio包的一部分割坠,因此API屬于C語言風(fēng)格。CFUUIDCreate 方法用來創(chuàng)建CFUUIDRef妒牙,并且可以獲得一個相應(yīng)的NSString彼哼,如下代碼:
CFUUIDRef cfuuid = CFUUIDCreate(kCFAllocatorDefault);NSString*cfuuidString = (NSString*)CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, cfuuid));
1
獲得的這個CFUUID值系統(tǒng)并沒有存儲。每次調(diào)用CFUUIDCreate湘今,系統(tǒng)都會返回一個新的唯一標(biāo)示符敢朱。如果你希望存儲這個標(biāo)示符,那么需要自己將其存儲到NSUserDefaults, Keychain, Pasteboard或其它地方。
NSUUID在iOS 6中才出現(xiàn)拴签,這跟CFUUID幾乎完全一樣孝常,只不過它是Objective-C接口。+ (id)UUID 是一個類方法蚓哩,調(diào)用該方法可以獲得一個UUID茫因。通過下面的代碼可以獲得一個UUID字符串:
NSString *uuid = [[NSUUID UUID] UUIDString];
1
跟CFUUID一樣,這個值系統(tǒng)也不會存儲杖剪,每次調(diào)用的時候都會獲得一個新的唯一標(biāo)示符。如果要存儲的話驰贷,你需要自己存儲盛嘿。在我讀取NSUUID時,注意到獲取到的這個值跟CFUUID完全一樣(不過也可能不一樣)
在iOS 5發(fā)布時括袒,uniqueIdentifier被棄用了次兆,這引起了廣大開發(fā)者需要尋找一個可以替代UDID,并且不受蘋果控制的方案锹锰。由此OpenUDID成為了當(dāng)時使用最廣泛的開源UDID替代方案芥炭。OpenUDID在工程中實現(xiàn)起來非常簡單,并且還支持一系列的廣告提供商恃慧。
OpenUDID利用了一個非常巧妙的方法在不同程序間存儲標(biāo)示符 — 在粘貼板中用了一個特殊的名稱來存儲標(biāo)示符园蝠。通過這種方法,別的程序(同樣使用了OpenUDID)知道去什么地方獲取已經(jīng)生成的標(biāo)示符(而不用再生成一個新的)痢士。而且根據(jù)貢獻者的代碼和方法彪薛,和一些開發(fā)者的經(jīng)驗,如果把使用了OpenUDID方案的應(yīng)用全部都刪除怠蹂,再重新獲取OpenUDID善延,此時的OpenUDID就跟以前的不一樣〕遣啵可見易遣,這種方法還是不保險。?
但是OpenUDID庫早已經(jīng)棄用了, 在其官方的博客中也指明了, 停止維護OpenUDID的原因是為了更好的向蘋果的舉措靠攏, 還指明了MAC Address不是一個好的選擇嫌佑。?
MAC(Medium/Media Access Control)地址屋摇,用來表示互聯(lián)網(wǎng)上每一個站點的標(biāo)識符澜薄,采用十六進制數(shù)表示,共六個字節(jié)(48位)摊册。其中肤京,前三個字節(jié)是由IEEE的注冊管理機構(gòu) RA負責(zé)給不同廠家分配的代碼(高位24位),也稱為“編制上唯一的標(biāo)識符” (Organizationally Unique Identifier),后三個字節(jié)(低位24位)由各廠家自行指派給生產(chǎn)的適配器接口忘分,稱為擴展標(biāo)識符(唯一性)棋枕。?
MAC地址在網(wǎng)絡(luò)上用來區(qū)分設(shè)備的唯一性,接入網(wǎng)絡(luò)的設(shè)備都有一個MAC地址妒峦,他們肯定都是不同的重斑,是唯一的。一部iPhone上可能有多個MAC地址肯骇,包括WIFI的窥浪、SIM的等,但是iTouch和iPad上就有一個WIFI的笛丙,因此只需獲取WIFI的MAC地址就好了漾脂,也就是en0的地址。?
形象的說胚鸯,MAC地址就如同我們身份證上的身份證號碼骨稿,具有全球唯一性。這樣就可以非常好的標(biāo)識設(shè)備唯一性姜钳,類似與蘋果設(shè)備的UDID號坦冠,通常的用途有:?
1)用于一些統(tǒng)計與分析目的,利用用戶的操作習(xí)慣和數(shù)據(jù)更好的規(guī)劃產(chǎn)品哥桥;?
2)作為用戶ID來唯一識別用戶辙浑,可以用游客身份使用app又能在服務(wù)器端保存相應(yīng)的信息,省去用戶名拟糕、密碼等注冊過程例衍。
2.如何使用Mac地址生成設(shè)備的唯一標(biāo)識呢?
主要分三種:?
1已卸、直接使用“MAC Address”?
2佛玄、使用“MD5(MAC Address)”?
3、使用“MD5(Mac Address+bundle_id)”獲得“機器+應(yīng)用”的唯一標(biāo)識(bundle_id 是應(yīng)用的唯一標(biāo)識)
iOS7之前累澡,因為Mac地址是唯一的梦抢, 一般app開發(fā)者會采取第3種方式來識別安裝對應(yīng)app的設(shè)備。為什么會使用它愧哟?在iOS5之前奥吩,都是使用UDID的,后來被禁用蕊梧。蘋果推薦使用UUID 但是也有諸多問題霞赫,從而使用MAC地址。而MAC地址跟UDID一樣肥矢,存在隱私問題端衰,現(xiàn)在蘋果新發(fā)布的iOS7上叠洗,如果請求Mac地址都會返回一個固定值,那么Mac Address+bundle_id這個值大家的設(shè)備都變成一致的啦旅东,跟UDID一樣相當(dāng)于被禁用, 所以Mac Address 是不能夠被使用為獲取設(shè)備唯一標(biāo)識的灭抑。
五.廣告標(biāo)示符(IDFA-identifierForIdentifier)
廣告標(biāo)示符,在同一個設(shè)備上的所有App都會取到相同的值抵代,是蘋果專門給各廣告提供商用來追蹤用戶而設(shè)的腾节。但好在Apple默認是允許追蹤的,而且一般用戶都不知道有這么個設(shè)置荤牍,所以基本上用來監(jiān)測推廣效果案腺,是戳戳有余了。?
它是iOS 6中另外一個新的方法康吵,提供了一個方法advertisingIdentifier劈榨,通過調(diào)用該方法會返回一個NSUUID實例,最后可以獲得一個UUID涎才,由系統(tǒng)存儲著的。
#importNSString*adId = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
1
2
不過即使這是由系統(tǒng)存儲的力九,但是有幾種情況下耍铜,會重新生成廣告標(biāo)示符。如果用戶完全重置系統(tǒng)((設(shè)置程序 -> 通用 -> 還原 -> 還原位置與隱私) 跌前,這個廣告標(biāo)示符會重新生成棕兼。另外如果用戶明確的還原廣告(設(shè)置程序-> 通用 -> 關(guān)于本機 -> 廣告 -> 還原廣告標(biāo)示符) ,那么廣告標(biāo)示符也會重新生成抵乓。?
關(guān)于廣告標(biāo)示符的還原伴挚,有一點需要注意:如果程序在后臺運行,此時用戶“還原廣告標(biāo)示符”灾炭,然后再回到程序中茎芋,此時獲取廣 告標(biāo)示符并不會立即獲得還原后的標(biāo)示符。必須要終止程序蜈出,然后再重新啟動程序田弥,才能獲得還原后的廣告標(biāo)示符。?
所以IDFA也不可以作為獲取唯一標(biāo)識的方法铡原,來識別用戶偷厦。
六.Vendor標(biāo)示符 (IDFV-identifierForVendor)
Vendor標(biāo)示符,是給Vendor標(biāo)識用戶用的燕刻,每個設(shè)備在所屬同一個Vender的應(yīng)用里只泼,都有相同的值。其中的Vender是指應(yīng)用提供商卵洗,但準(zhǔn)確點說请唱,是通過BundleID的反轉(zhuǎn)的前兩部分進行匹配,如果相同就是同一個Vender,例如對于com.taobao.app1, com.taobao.app2 這兩個BundleID來說籍滴,就屬于同一個Vender酪夷,共享同一個IDFV的值。和IDFA不同的是孽惰,IDFV的值是一定能取到的晚岭,所以非常適合于作為內(nèi)部用戶行為分析的主id,來標(biāo)識用戶勋功,替代OpenUDID坦报。?
它是iOS 6中新增的,跟advertisingIdentifier一樣狂鞋,該方法返回的是一個 NSUUID對象片择,可以獲得一個UUID。如果滿足條件“相同的一個程序里面-相同的vendor-相同的設(shè)備”骚揍,那么獲取到的這個屬性值就不會變字管。如果是“相同的程序-相同的設(shè)備-不同的vendor,或者是相同的程序-不同的設(shè)備-無論是否相同的vendor”這樣的情況信不,那么這個值是不會相同的嘲叔。
? ? NSString *strIDFV = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
1
但是如果用戶將屬于此Vender的所有App卸載,則IDFV的值會被重置抽活,即再重裝此Vender的App硫戈,IDFV的值和之前不同。
推送token+bundle_id的方法:?
1下硕、應(yīng)用中增加推送用來獲取token?
2丁逝、獲取應(yīng)用bundle_id?
3、根據(jù)token+bundle_id進行散列運算
apple push token保證設(shè)備唯一梭姓,但必須有網(wǎng)絡(luò)情況下才能工作霜幼,該方法并不是依賴于設(shè)備本身,而是依賴于apple push機制誉尖,所以當(dāng)蘋果push做出改變時, 你獲取所謂的唯一標(biāo)識也就隨之失效了辛掠。所以此方法還是不可取的。
八. NSUUID, CFUUID, IDFA, IDFV獲取的標(biāo)識對比
首次運行
NSUUID:9D820D3A-4429-4918-97F7-A69588B388A4?
CFUUID:80F961D0-1E6A-4ECD-A0A9-F58ED858FE20?
IDFA:687E6A90-50A3-4424-871C-BE255D050AFD?
IDFV:8E740A99-283B-4F6A-87EF-443FB7778488
二次運行
NSUUID:23AB8D3D-4F1D-45E2-8BD7-83B451125326?
CFUUID:14DDBFCF-67A6-46B7-BB48-4EF2ADC5429F?
IDFA:687E6A90-50A3-4424-871C-BE255D050AFD?
IDFV:8E740A99-283B-4F6A-87EF-443FB7778488
卸載后, 重新安裝運行
NSUUID:BD934F9C-B7EC-4BD1-B65E-964C66537CAB?
CFUUID:29654DE0-AC93-40F9-98AB-1E10A271AF8D?
IDFA:687E6A90-50A3-4424-871C-BE255D050AFD?
IDFV:8E740A99-283B-4F6A-87EF-443FB7778488
重啟后運行
NSUUID:82711557-3A17-4B82-8F18-09AADF9DD37B?
CFUUID:FFBC73EC-CFBE-414C-870E-77C0714E0347?
IDFA:687E6A90-50A3-4424-871C-BE255D050AFD?
IDFV:8E740A99-283B-4F6A-87EF-443FB7778488
說了這么多, 才發(fā)現(xiàn)原來沒有一種方法是可行的释牺。沒錯, 其實自從蘋果廢除UDID后, 就不能達到獲取設(shè)備真正的唯一標(biāo)識了萝衩。因為這些方法中導(dǎo)致獲取的唯一標(biāo)示產(chǎn)生改變的原因, 或是重新調(diào)用方法, 或是重啟設(shè)備, 或是卸載應(yīng)用, 或是還原某些標(biāo)識, 或者刷新系統(tǒng)…?
所以, 不能達到從根本上獲取唯一標(biāo)識, 我們只能做到盡可能接近。下面是我用過的方法没咙。
我用的方法是將獲取的UUID永久存儲在設(shè)備的KeyChain中, 這個方法在應(yīng)用第一次啟動時, 將獲取的UUID存儲進KeyChain中, 每次取的時候, 檢查本地鑰匙串中有沒有, 如果沒有則需要將獲取的UUID存儲進去猩谊。當(dāng)你重啟設(shè)備, 卸載應(yīng)用再次安裝,都不影響, 只是當(dāng)設(shè)備刷機時, KeyChain會清空, 才會消失, 才會失效。?
不只是這一種方法, 你也可以保存除UUID之外,其他合適的標(biāo)識, 但利用KeyChain去存儲標(biāo)識的方式應(yīng)該是最接近的祭刚。
利用keyChain和UUID永久獲得設(shè)備的唯一標(biāo)識
開發(fā)者可以在應(yīng)用第一次啟動時調(diào)用一次牌捷,然后將該串存儲起來墙牌,以便以后替代UDID來使用。但是暗甥,如果用戶刪除該應(yīng)用再次安裝時喜滨,又會生成新的字符串,所以不能保證唯一識別該設(shè)備撤防。這就需要各路高手想出各種解決方案虽风。所以,之前很多應(yīng)用就采用MAC Address寄月。但是現(xiàn)在如果用戶升級到iOS7(及其以后的蘋果系統(tǒng))后辜膝,他們機子的MAC Address就是一樣的,沒辦法做區(qū)分漾肮,只能棄用此方法厂抖,重新使用UUID來標(biāo)識。如果使用UUID克懊,就要考慮應(yīng)用被刪除后再重新安裝時的處理忱辅。
一、在應(yīng)用間利用KeyChain共享數(shù)據(jù)?
我們可以把KeyChain理解為一個Dictionary谭溉,所有數(shù)據(jù)都以key-value的形式存儲墙懂,可以對這個Dictionary進行add、update夜只、get燕差、delete這四個操作板丽。對于每一個應(yīng)用來說,KeyChain都有兩個訪問區(qū)苞氮,私有區(qū)和公共區(qū)谈为。私有區(qū)是一個sandbox旅挤,本程序存儲的任何數(shù)據(jù)都對其他程序不可見。而要想在將存儲的內(nèi)容放在公共區(qū)伞鲫,需要先聲明公共區(qū)的名稱粘茄,官方文檔管這個名稱叫“keychain access group”,聲明的方法是新建一個plist文件秕脓,名字隨便起柒瓣,內(nèi)容如下:
“yourAppID.com.yourCompany.whatever”就是你要起的公共區(qū)名稱,除了whatever字段可以隨便定之外吠架,其他的都必須如實填寫芙贫。這個文件的路徑要配置在 Project->build setting->Code Signing Entitlements里,否則公共區(qū)無效傍药,配置好后磺平,須用你正式的證書簽名編譯才可通過魂仍,否則xcode會彈框告訴你code signing有問題。所以拣挪,蘋果限制了你只能同公司的產(chǎn)品共享KeyChain數(shù)據(jù)擦酌,別的公司訪問不了你公司產(chǎn)品的KeyChain。
二菠劝、保存私密信息?
iOS的keychain服務(wù)提供了一種安全的保存私密信息(密碼赊舶,序列號,證書等)的方式闸英,每個ios程序都有一個獨立的keychain存儲锯岖。相對于NSUserDefaults、文件保存等一般方式甫何,keychain保存更為安全出吹,而且keychain里保存的信息不會因App被刪除而丟失,所以在重裝App后辙喂,keychain里的數(shù)據(jù)還能使用捶牢。
首先, 我先推薦兩篇文章,里面介紹了如利用keyChain和UUID永久獲得設(shè)備的唯一標(biāo)識,?Classes/KeychainItemWrapper.m和?如何使用KeyChain保存和獲取UDID?。?
然后, 再介紹下我使用的方法以及封裝的工具類, 在應(yīng)用里使用使用keyChain巍耗,我們需要導(dǎo)入Security.framework秋麸。下面介紹下, 我在其他庫基礎(chǔ)上封裝的一個獲取唯一標(biāo)識的工具類:
#import#importNSString *constKEY_UDID_INSTEAD = @"com.myapp.udid.test";@interfaceLZKeychain : NSObject/**
本方法是得到 UUID 后存入系統(tǒng)中的 keychain 的方法
不用添加 plist 文件
程序刪除后重裝,仍可以得到相同的唯一標(biāo)示
但是,只有手機(設(shè)備)刷機后,系統(tǒng)中的鑰匙串會被清空,此時本方法失效炬太。這種情況是我們手機(設(shè)備)所有的設(shè)置都要重新進行操作灸蟆。
我個人認為 (UUID + keychain) 的解決方法是目前最好的解決方法。