IOS 獲取設(shè)備唯一標(biāo)識的方法(KeyChain+UUID)

在iOS系統(tǒng)中碍论,獲取設(shè)備唯一標(biāo)識的方法有很多(可直接看第八點(diǎn)句柠,比較靠譜):

一.UDID(Unique Device Identifier)

UDID的全稱是Unique Device Identifier榕暇,它就是蘋果IOS設(shè)備的唯一識別碼依沮,它由40個字符的字母和數(shù)字組成(越獄的設(shè)備通過某些工具可以改變設(shè)備的UDID)恃逻。移動網(wǎng)絡(luò)可利用UDID來識別移動設(shè)備,但是晤锹,從IOS5.0(2011年8月份)開始,蘋果宣布將不再支持用uniqueIdentifier方法獲取設(shè)備的UDID彤委,iOS5以下是可以用的鞭铆。在2013年3月21日蘋果已經(jīng)通知開發(fā)者:從2013年5月1日起,訪問UIDIDs的程序?qū)⒉辉俦粚徍送ㄟ^焦影,替代的方案是開發(fā)者應(yīng)該使用“在iOS 6中介紹的Vendor或Advertising標(biāo)示符”车遂。所以UDID是絕對不能用啦。

二.UUID(Universally Unique Identifier)
UUID是Universally Unique Identifier的縮寫斯辰,中文意思是通用唯一識別碼舶担。它是讓分布式系統(tǒng)中的所有元素,都能有唯一的辨識資訊椒涯,而不需要透過中央控制端來做辨識資訊的指定柄沮。這樣,每個人都可以建立不與其它人沖突的 UUID废岂。在此情況下祖搓,就不需考慮數(shù)據(jù)庫建立時的名稱重復(fù)問題。蘋果公司建議使用UUID為應(yīng)用生成唯一標(biāo)識字符串湖苞。

三.MAC Address

這個MAC地址是指什么拯欧?有什么用?
MAC(Medium/Media Access Control)地址财骨,用來表示互聯(lián)網(wǎng)上每一個站點(diǎn)的標(biāo)識符镐作,采用十六進(jìn)制數(shù)表示,共六個字節(jié)(48位)隆箩。其中该贾,前三個字節(jié)是由IEEE的注冊管理機(jī)構(gòu) RA負(fù)責(zé)給不同廠家分配的代碼(高位24位),也稱為“編制上唯一的標(biāo)識符” (Organizationally Unique Identifier)捌臊,后三個字節(jié)(低位24位)由各廠家自行指派給生產(chǎn)的適配器接口杨蛋,稱為擴(kuò)展標(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)計(jì)與分析目的,利用用戶的操作習(xí)慣和數(shù)據(jù)更好的規(guī)劃產(chǎn)品署尤;2)作為用戶ID來唯一識別用戶耙替,可以用游客身份使用app又能在服務(wù)器端保存相應(yīng)的信息,省去用戶名曹体、密碼等注冊過程俗扇。

那么,如何使用Mac地址生成設(shè)備的唯一標(biāo)識呢箕别?主要分三種:
1铜幽、直接使用“MAC Address”
2、使用“MD5(MAC Address)”
3串稀、使用“MD5(Mac Address+bundle_id)”獲得“機(jī)器+應(yīng)用”的唯一標(biāo)識(bundle_id 是應(yīng)用的唯一標(biāo)識)

iOS7之前除抛,因?yàn)镸ac地址是唯一的, 一般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)于被禁用。那么绽族,要怎么標(biāo)識設(shè)備唯一呢?

關(guān)于為什么用mac地址作為手機(jī)的唯一標(biāo)識,請參考知乎討論

四.OPEN UDID
OPEN UDID衩藤,沒有用到MAC地址吧慢,同時能保證同一臺設(shè)備上的不同應(yīng)用使用同一個OpenUDID,只要用戶設(shè)備上有一個使用了OpenUDID的應(yīng)用存在時赏表,其他后續(xù)安裝的應(yīng)用如果獲取OpenUDID检诗,都將會獲得第一個應(yīng)用生成的那個。但是根據(jù)貢獻(xiàn)者的代碼和方法瓢剿,和一些開發(fā)者的經(jīng)驗(yàn)逢慌,如果把使用了OpenUDID方案的應(yīng)用全部都刪除,再重新獲取OpenUDID间狂,此時的OpenUDID就跟以前的不一樣攻泼。可見鉴象,這種方法還是不保險忙菠。

五.廣告標(biāo)示符(IDFA-identifierForIdentifier)
廣告標(biāo)示符,是iOS 6中另外一個新的方法纺弊,提供了一個方法advertisingIdentifier牛欢,通過調(diào)用該方法會返回一個NSUUID實(shí)例,最后可以獲得一個UUID淆游,由系統(tǒng)存儲著的傍睹。不過即使這是由系統(tǒng)存儲的,但是有幾種情況下犹菱,會重新生成廣告標(biāo)示符拾稳。如果用戶完全重置系統(tǒng)((設(shè)置程序 -> 通用 -> 還原 -> 還原位置與隱私) ,這個廣告標(biāo)示符會重新生成已亥。另外如果用戶明確的還原廣告(設(shè)置程序-> 通用 -> 關(guān)于本機(jī) -> 廣告 -> 還原廣告標(biāo)示符) 熊赖,那么廣告標(biāo)示符也會重新生成。關(guān)于廣告標(biāo)示符的還原虑椎,有一點(diǎn)需要注意:如果程序在后臺運(yùn)行震鹉,此時用戶“還原廣告標(biāo)示符”,然后再回到程序中捆姜,此時獲取廣 告標(biāo)示符并不會立即獲得還原后的標(biāo)示符传趾。必須要終止程序,然后再重新啟動程序泥技,才能獲得還原后的廣告標(biāo)示符浆兰。

六.Vindor標(biāo)示符 (IDFV-identifierForVendor)

Vindor標(biāo)示符,也是在iOS 6中新增的,跟advertisingIdentifier一樣簸呈,該方法返回的是一個 NSUUID對象榕订,可以獲得一個UUID。如果滿足條件“相同的一個程序里面-相同的vindor-相同的設(shè)備”蜕便,那么獲取到的這個屬性值就不會變劫恒。如果是“相同的程序-相同的設(shè)備-不同的vindor,或者是相同的程序-不同的設(shè)備-無論是否相同的vindor”這樣的情況轿腺,那么這個值是不會相同的两嘴。

七.推送token+bundle_id

推送token+bundle_id的方法:
1、應(yīng)用中增加推送用來獲取token
2族壳、獲取應(yīng)用bundle_id
3憔辫、根據(jù)token+bundle_id進(jìn)行散列運(yùn)算

apple push token保證設(shè)備唯一,但必須有網(wǎng)絡(luò)情況下才能工作仿荆,該方法不依賴于設(shè)備本身贰您,但依賴于apple push,而蘋果push有時候會抽風(fēng)的拢操。

八,利用keyChain和UUID永久獲得設(shè)備的唯一標(biāo)識(目前比較靠譜的方法)

開發(fā)者可以在應(yīng)用第一次啟動時調(diào)用一 次枉圃,然后將該串存儲起來,以便以后替代UDID來使用庐冯。但是孽亲,如果用戶刪除該應(yīng)用再次安裝時,又會生成新的字符串展父,所以不能保證唯一識別該設(shè)備返劲。這就需要各路高手想出各種解決方案。所以栖茉,之前很多應(yīng)用就采用MAC Address篮绿。但是現(xiàn)在如果用戶升級到iOS7(及其以后的蘋果系統(tǒng))后,他們機(jī)子的MAC Address就是一樣的吕漂,沒辦法做區(qū)分亲配,只能棄用此方法,重新使用UUID來標(biāo)識惶凝。如果使用UUID吼虎,就要考慮應(yīng)用被刪除后再重新安裝時的處理。

那么什么是鑰匙串呢:

一苍鲜、在應(yīng)用間利用KeyChain共享數(shù)據(jù)

   我們可以把KeyChain理解為一個Dictionary思灰,所有數(shù)據(jù)都以key-value的形式存儲,可以對這個Dictionary進(jìn)行add混滔、update洒疚、get歹颓、delete這四個操作。對于每一個應(yīng)用來說油湖,KeyChain都有兩個訪問區(qū)巍扛,私有區(qū)和公共區(qū)。私有區(qū)是一個sandbox乏德,本程序存儲的任何數(shù)據(jù)都對其他程序不可見电湘。而要想在將存儲的內(nèi)容放在公共區(qū),需要先聲明公共區(qū)的名稱鹅经,官方文檔管這個名稱叫“keychain access group”,聲明的方法是新建一個plist文件怎诫,名字隨便起瘾晃,內(nèi)容如下:
20131106010851813.jpeg

“yourAppID.com.yourCompany.whatever”就是你要起的公共區(qū)名稱,除了whatever字段可以隨便定之外幻妓,其他的都必須如實(shí)填寫蹦误。這個文件的路徑要配置在 Project->build setting->Code Signing Entitlements里,否則公共區(qū)無效肉津,配置好后强胰,須用你正式的證書簽名編譯才可通過,否則xcode會彈框告訴你code signing有問題妹沙。所以偶洋,蘋果限制了你只能同公司的產(chǎn)品共享KeyChain數(shù)據(jù),別的公司訪問不了你公司產(chǎn)品的KeyChain距糖。

二玄窝、保存私密信息
iOS的keychain服務(wù)提供了一種安全的保存私密信息(密碼,序列號悍引,證書等)的方式恩脂,每個ios程序都有一個獨(dú)立的keychain存儲。相對于NSUserDefaults趣斤、文件保存等一般方式俩块,keychain保存更為安全,而且keychain里保存的信息不會因App被刪除而丟失浓领,所以在重裝App后玉凯,keychain里的數(shù)據(jù)還能使用。

在應(yīng)用里使用使用keyChain联贩,我們需要導(dǎo)入Security.framework 壮啊,keychain的操作接口聲明在頭文件SecItem.h里。直接使用SecItem.h里方法操作keychain撑蒜,需要寫的代碼較為復(fù)雜歹啼,為減輕咱們程序員的開發(fā)玄渗,這里封裝了一個方法,

實(shí)現(xiàn)如下:

//
//  KKUUIDManager.h
//


#import <Foundation/Foundation.h>

@interface KKUUIDManager : NSObject
+(void)saveUUID:(NSString *)uuid;

+(id)readUUID;

+(void)deleteUUID;
@end
//
//  KKUUIDManager.m
//


#import "KKUUIDManager.h"
#import "KKKeyChain.h"

@implementation KKUUIDManager

#define KEY_IN_KEYCHAIN [NSBundle mainBundle].bundleIdentifier
#define KEY_UUID [NSString stringWithFormat:@"%@.uuid", KEY_IN_KEYCHAIN]

+(void)saveUUID:(NSString *)uuid
{
    [KKKeyChain save:KEY_IN_KEYCHAIN data:@{KEY_UUID:uuid}];
}

+(id)readUUID
{
    NSMutableDictionary *usernameUuidPairs = (NSMutableDictionary *)[KKKeyChain load:KEY_IN_KEYCHAIN];
    return [usernameUuidPairs objectForKey:KEY_UUID];
}

+(void)deleteUUID
{
    [KKKeyChain delete:KEY_IN_KEYCHAIN];
}
@end
//
//  KKKeyChain.h
//


#import <Foundation/Foundation.h>

@interface KKKeyChain : NSObject
+ (NSMutableDictionary *)getKeychainQuery:(NSString *)service ;

+ (void)save:(NSString *)service data:(id)data;

+ (id)load:(NSString *)service;

+ (void)delete:(NSString *)service;
@end
//
//  KKKeyChain.m
//


#import "KKKeyChain.h"

@implementation KKKeyChain
+ (NSMutableDictionary *)getKeychainQuery:(NSString *)service {
    return [NSMutableDictionary dictionaryWithObjectsAndKeys:
            (__bridge_transfer id)kSecClassGenericPassword,(__bridge_transfer id)kSecClass,
            service, (__bridge_transfer id)kSecAttrService,
            service, (__bridge_transfer id)kSecAttrAccount,
            (__bridge_transfer id)kSecAttrAccessibleAfterFirstUnlock,(__bridge_transfer id)kSecAttrAccessible,
            nil];
}

+ (void)save:(NSString *)service data:(id)data {
    //Get search dictionary
    NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
    //Delete old item before add new item
    CFDictionaryRef aRef = (__bridge_retained CFDictionaryRef)keychainQuery;
    SecItemDelete(aRef/*(__bridge_retained CFDictionaryRef)keychainQuery*/);
    //Add new object to search dictionary(Attention:the data format)
    [keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(__bridge_transfer id)kSecValueData];
    //Add item to keychain with the search dictionary
    SecItemAdd(aRef/*(__bridge_retained CFDictionaryRef)keychainQuery*/, NULL);
}

+ (id)load:(NSString *)service {
    id ret = nil;
    NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
    //Configure the search setting
    [keychainQuery setObject:(id)kCFBooleanTrue forKey:(__bridge_transfer id)kSecReturnData];
    [keychainQuery setObject:(__bridge_transfer id)kSecMatchLimitOne forKey:(__bridge_transfer id)kSecMatchLimit];
    CFDataRef keyData = NULL;
    if (SecItemCopyMatching((__bridge_retained CFDictionaryRef)keychainQuery, (CFTypeRef *)&keyData) == noErr) {
        @try {
            ret = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge_transfer NSData *)keyData];
        } @catch (NSException *e) {
            NSLog(@"Unarchive of %@ failed: %@", service, e);
        } @finally {
        }
    }
    return ret;
}

+ (void)delete:(NSString *)service {
    NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
    SecItemDelete((__bridge_retained CFDictionaryRef)keychainQuery);
}
@end

然后再任意地方調(diào)用以下獲取

NSString *uuid = [KKUUIDManager readUUID];
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末狸眼,一起剝皮案震驚了整個濱河市藤树,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌拓萌,老刑警劉巖岁钓,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異微王,居然都是意外死亡屡限,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門炕倘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來钧大,“玉大人,你說我怎么就攤上這事罩旋“⊙耄” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵涨醋,是天一觀的道長瓜饥。 經(jīng)常有香客問我,道長浴骂,這世上最難降的妖魔是什么乓土? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮溯警,結(jié)果婚禮上帐我,老公的妹妹穿的比我還像新娘。我一直安慰自己愧膀,他們只是感情好拦键,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著檩淋,像睡著了一般芬为。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蟀悦,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天媚朦,我揣著相機(jī)與錄音,去河邊找鬼日戈。 笑死询张,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的浙炼。 我是一名探鬼主播份氧,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼唯袄,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蜗帜?” 一聲冷哼從身側(cè)響起恋拷,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎厅缺,沒想到半個月后蔬顾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡湘捎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年诀豁,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片窥妇。...
    茶點(diǎn)故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡舷胜,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出秩伞,到底是詐尸還是另有隱情,我是刑警寧澤欺矫,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布纱新,位于F島的核電站,受9級特大地震影響穆趴,放射性物質(zhì)發(fā)生泄漏脸爱。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一未妹、第九天 我趴在偏房一處隱蔽的房頂上張望簿废。 院中可真熱鬧,春花似錦络它、人聲如沸族檬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽单料。三九已至,卻和暖如春点楼,著一層夾襖步出監(jiān)牢的瞬間扫尖,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工掠廓, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留换怖,地道東北人。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓蟀瞧,卻偏偏與公主長得像沉颂,于是被迫代替她去往敵國和親条摸。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評論 2 345

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