【iOS】獲取設(shè)備唯一標(biāo)識符

最近項目中要用到設(shè)備的唯一標(biāo)識符肮塞,于是了解了一下這方面的知識襟齿。設(shè)備的唯一標(biāo)識符具體可以分為如下幾種:
1、UDID
2枕赵、MAC
3猜欺、IDFA
4、IDFV
5拷窜、UUID

下面我們來具體分析下每種標(biāo)識獲取的可行性和使用的利弊:

1开皿、UDID(Unique Device Identifier Description)

UDID 是由子母和數(shù)字組成的40個字符串的序號,用來區(qū)別每一個唯一的iOS設(shè)備篮昧,這些編碼看起來是隨機的赋荆,但實際上是跟硬件設(shè)備特點相關(guān)聯(lián)的。我們平時用開發(fā)者賬號在設(shè)備上安裝測試應(yīng)用時懊昨,往開發(fā)者賬號上添加的就是設(shè)備的UDID窄潭。
iOS 2.0 版本以后,蘋果提供了一個獲取設(shè)備唯一標(biāo)識符的方法

NSString *UDID = [[UIDevice currentDevice] uniqueIdentifier] 疚颊;

通過該方法我們可以獲取設(shè)備的序列號狈孔,這個也是目前為止唯一可以確認(rèn)唯一的標(biāo)示符。由于UDID是跟設(shè)備唯一對應(yīng)的材义,許多開發(fā)者試圖通過UDID獲取到用戶的真實姓名均抽、密碼、地址等隱私數(shù)據(jù)其掂。為了避免引起麻煩油挥,蘋果在iOS 5.0 的時候,廢除了UDID的代碼獲取權(quán)限】畎荆現(xiàn)在應(yīng)用試圖獲取UDID已被禁止且不允許上架深寥。

當(dāng)然,目前想要獲取UDID也并不是全無辦法贤牛,不過過程可能會復(fù)雜很多惋鹅,這里就不做贅述了,有興趣的朋友可以參考這篇博客:通過Safari獲取UDID

2殉簸、MAC(Medium/Media Access Control)

Mac地址是用來表示互聯(lián)網(wǎng)上每一個站點的標(biāo)識符闰集,采用十六進制數(shù)表示,共六個字節(jié)(48位)般卑。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è)備唯一性布蔗。UDID被禁用后,因為每個設(shè)備上的Mac地址是唯一的浪腐,所以大部分的app開發(fā)者都開始使用Mac地址來代替UDID纵揍。獲取Mac地址的方法如下:

 - (NSString *) getMacAddress  
    {  
        int mib[6];  
        size_t len;  
        charchar *buf;  
        unsigned charchar *ptr;  
        struct if_msghdr *ifm;  
        struct sockaddr_dl *sdl;  
   
        mib[0] = CTL_NET;  
        mib[1] = AF_ROUTE;  
        mib[2] = 0;  
        mib[3] = AF_LINK;  
        mib[4] = NET_RT_IFLIST;  
        
        if ((mib[5] = if_nametoindex("en0")) == 0) {  
            printf("Error: if_nametoindex error/n");  
            return NULL;  
        }  
        
        if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {  
            printf("Error: sysctl, take 1/n");  
            return NULL;  
        }  
          
        if ((buf = malloc(len)) == NULL) {  
            printf("Could not allocate memory. error!/n");  
            return NULL;  
        }  
 
        if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {  
            printf("Error: sysctl, take 2");  
            return NULL;  
        }  

        ifm = (struct if_msghdr *)buf;  
        sdl = (struct sockaddr_dl *)(ifm + 1);  
        ptr = (unsigned charchar *)LLADDR(sdl);  
        NSString *outstring = [NSString stringWithFormat:@"%02x:%02x:%02x:%02x:%02x:%02x", *ptr, *(ptr+1), *(ptr+2), *(ptr+3), *(ptr+4), *(ptr+5)];  
        free(buf);  

        return [outstring uppercaseString]; 
    }  

但MAC地址跟UDID一樣,存在隱私問題议街,所以在 iOS 7.0 之后泽谨,Mac地址再次遭到蘋果的無情封殺。如果使用之前的方法請求Mac地址都會返回一個固定值 02:00:00:00:00:00特漩,這一條路再次被堵死吧雹。

3、IDFA (Identifier For Advertising)

這是 iOS 6.0中提供的一個新方法涂身,在同一個設(shè)備上的所有App都會取到相同的值雄卷,是蘋果專門給各廣告提供商用來追蹤用戶而設(shè)的。獲取idfa的方法如下:

NSString *idfa = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];

但是IDFA并不是唯一不變的蛤售,如果用戶完全重置系統(tǒng)(設(shè)置程序 -> 通用 -> 還原 -> 還原位置與隱私) 憔杨,這個廣告標(biāo)示符會重新生成挤渔。另外如果用戶明確的還原廣告(設(shè)置程序-> 通用 -> 關(guān)于本機 -> 廣告 -> 還原廣告標(biāo)示符) ,那么廣告標(biāo)示符也會重新生成肛根。在iOS 10.0以后如果用戶打開限制廣告跟蹤(設(shè)置程序-> 通用 -> 關(guān)于本機 -> 廣告 -> 限制廣告跟蹤)缀壤,則獲取到的IDFA為一個固定值00000000-0000-0000-0000-000000000000钦睡。因此憔鬼,通過IDFA也無法唯一標(biāo)識一個設(shè)備吉嚣。

4、IDFV(Identifier For Vendor)

IDFV是給Vendor標(biāo)識用戶用的炒嘲,每個設(shè)備在所屬同一個Vendor的應(yīng)用里谈竿,都有相同的值。其中的Vendor是指應(yīng)用提供商摸吠,準(zhǔn)確的說,是通過BundleID的反轉(zhuǎn)的前兩部分進行匹配嚎花,如果相同就是同一個Vendor寸痢,例如對于com.abc.app1, com.abc.app2 這兩個BundleID來說,就屬于同一個Vendor紊选,共享同一個IDFV的值啼止。當(dāng)然道逗,對于同一個設(shè)備不同Vendor的話,獲取到的值是不同的献烦。和IDFA不同的是滓窍,IDFV的值是一定能取到的。它是iOS 6中新增的巩那,獲取方法如下:

NSString *strIDFV = [[[UIDevice currentDevice] identifierForVendor] UUIDString];

但是使用IDFV也會存在一些問題吏夯。如果用戶將屬于此Vendor的所有App卸載,則IDFV的值會被重置即横,即再重裝此Vendor的App噪生,IDFV的值也會和之前的不同。

5东囚、UUID(Universally Unique Identifier)

UUID是Universally Unique Identifier的縮寫跺嗽,中文意思是通用唯一識別碼。它是蘋果提供的一個獲取大隨機數(shù)的方法页藻,據(jù)說UUID隨機數(shù)算法得到的數(shù)重復(fù)概率為170億分之一桨嫁。這樣,每個人都可以建立不與其它人沖突的 UUID份帐。

1)CFUUID
從iOS 2.0開始璃吧,CFUUID就已經(jīng)出現(xiàn)了。它是CoreFoundation包的一部分弥鹦,因此API屬于C語言風(fēng)格肚逸。獲取方法參考如下代碼:

CFUUIDRef cfuuid = CFUUIDCreate(kCFAllocatorDefault);
NSString *cfuuidString = (NSString*)CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, cfuuid));

2)NSUUID
NSUUID在iOS 6.0中才出現(xiàn),這跟CFUUID幾乎完全一樣彬坏,只不過它是Objective-C接口朦促。通過下面的代碼可以獲得一個UUID字符串:

NSString *uuid = [[NSUUID UUID] UUIDString];

蘋果公司建議使用UUID為應(yīng)用生成唯一標(biāo)識字符串。但是獲得的UUID值系統(tǒng)沒有存儲, 而且每次調(diào)用得到UUID栓始,系統(tǒng)都會返回一個新的唯一標(biāo)示符务冕。如果你希望存儲這個標(biāo)示符,那么需要自己將其存儲到NSUserDefaults, Keychain, Pasteboard或其它地方幻赚。

6禀忆、總結(jié)

說了這么多, 才發(fā)現(xiàn)原來沒有一種方法是可行的。沒錯, 其實自從蘋果廢除UDID后, 就不能達到獲取設(shè)備真正的唯一標(biāo)識了落恼。因為這些方法中導(dǎo)致獲取的唯一標(biāo)示產(chǎn)生改變的原因, 或是重新調(diào)用方法, 或是重啟設(shè)備, 或是卸載應(yīng)用, 或是還原某些標(biāo)識, 或者刷新系統(tǒng)…

所以, 我們不能達到從根本上獲取唯一標(biāo)識,只能做到盡可能接近箩退。下面是我自己的一套獲取設(shè)備唯一標(biāo)識的方法。希望能夠?qū)Υ蠹矣兴鶐椭亚1疚闹徽迟N部分邏輯代碼戴涝,需要查看完整代碼的可以到GitHub下載:GitHub地址

//獲取UQID
+ (NSString *)getUQID
{
    //從本地沙盒取
    NSString *uqid = [[NSUserDefaults standardUserDefaults] objectForKey:UQID_KEY];
    
    if (!uqid) {
        //從keychain取
        uqid = (NSString *)[YDKeyChain readObjectForKey:UQID_KEY];
        
        if (uqid) {
            [[NSUserDefaults standardUserDefaults] setObject:uqid forKey:UQID_KEY];
            [[NSUserDefaults standardUserDefaults] synchronize];
            
        } else {
            //從pasteboard取
            UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
            id data = [pasteboard dataForPasteboardType:UQID_KEY];
            if (data) {
                uqid = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
            }
            
            if (uqid) {
                [[NSUserDefaults standardUserDefaults] setObject:uqid forKey:UQID_KEY];
                [[NSUserDefaults standardUserDefaults] synchronize];
                [YDKeyChain saveObject:uqid forKey:UQID_KEY];
                
            } else {
                
                //獲取idfa
                uqid = [self getIDFA];
                
                //idfa獲取失敗的情況,獲取idfv
                if (!uqid || [uqid isEqualToString:@"00000000-0000-0000-0000-000000000000"]) {
                    uqid = [self getIDFV];
                    
                    //idfv獲取失敗的情況,獲取uuid
                    if (!uqid) {
                        uqid = [self getUUID];
                    }
                }
                
                [[NSUserDefaults standardUserDefaults] setObject:uqid forKey:UQID_KEY];
                [[NSUserDefaults standardUserDefaults] synchronize];
                
                [YDKeyChain saveObject:uqid forKey:UQID_KEY];
                
                UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
                NSData *data = [uqid dataUsingEncoding:NSUTF8StringEncoding];
                [pasteboard setData:data forPasteboardType:UQID_KEY];
                
            }
        }
    }
    return uqid;
}

這套方案既防止了IDFA獲取失敗的情況啥刻,也能最大限度保證同一個設(shè)備上的不同應(yīng)用獲取到的標(biāo)識符為同一標(biāo)識符奸鸯。除開用戶重置手機的情況,其他情況下基本都可以保證取到相同的設(shè)備標(biāo)識符可帽,相對來說是比較安全的娄涩。當(dāng)然,由于使用到了IDFA映跟,在上傳Appstore時必須在iTunes Connect中的上傳頁面進行相應(yīng)的設(shè)置蓄拣,否則上傳應(yīng)用審核的時候會出現(xiàn)錯誤。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末申窘,一起剝皮案震驚了整個濱河市弯蚜,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌剃法,老刑警劉巖碎捺,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異贷洲,居然都是意外死亡收厨,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門优构,熙熙樓的掌柜王于貴愁眉苦臉地迎上來诵叁,“玉大人,你說我怎么就攤上這事钦椭∨《睿” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵彪腔,是天一觀的道長侥锦。 經(jīng)常有香客問我,道長德挣,這世上最難降的妖魔是什么恭垦? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮格嗅,結(jié)果婚禮上番挺,老公的妹妹穿的比我還像新娘。我一直安慰自己屯掖,他們只是感情好玄柏,可當(dāng)我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著贴铜,像睡著了一般禁荸。 火紅的嫁衣襯著肌膚如雪右蒲。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天赶熟,我揣著相機與錄音,去河邊找鬼陷嘴。 笑死映砖,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的灾挨。 我是一名探鬼主播邑退,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼劳澄!你這毒婦竟也來了地技?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤秒拔,失蹤者是張志新(化名)和其女友劉穎莫矗,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體砂缩,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡作谚,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了庵芭。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片妹懒。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖双吆,靈堂內(nèi)的尸體忽然破棺而出眨唬,到底是詐尸還是另有隱情,我是刑警寧澤好乐,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布匾竿,位于F島的核電站,受9級特大地震影響曹宴,放射性物質(zhì)發(fā)生泄漏搂橙。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一笛坦、第九天 我趴在偏房一處隱蔽的房頂上張望区转。 院中可真熱鬧,春花似錦版扩、人聲如沸废离。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蜻韭。三九已至悼尾,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間肖方,已是汗流浹背闺魏。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留俯画,地道東北人析桥。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像艰垂,于是被迫代替她去往敵國和親泡仗。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容