前言:
1 .為了統(tǒng)計和檢測應用的使用數(shù)據(jù)扁掸,幾乎每家公司都有獲取唯一標識的業(yè)務需求蝴韭,在iOS5以前獲取唯一標識,可以獲取到系統(tǒng)提供的方法UDID(Unique Device Identifier)刀脏,后來被出于用戶隱私的考慮被Apple官方禁止掉了。于是宝踪,大家開始在iOS6中使用 MAC 地址(Medium/Media Access Control) ,后來又被Apple官方在iOS7中禁止掉了祟印。蘋果及其國外的IT公司都會比較注重用戶隱私肴沫,所以今后一但有比較靠譜的獲取唯一標示的方法放出,蘋果肯定會封堵蕴忆。
2.** ** 在非越獄的手機上獲取某個硬件信息生成唯一標識颤芬,第一只能找到蘋果的漏洞,第二就是調用一些私有接口套鹅,顯然這兩條路都比較艱難站蝠,并不可持續(xù)發(fā)展,所以網上大部分的唯一標識都是從操作系統(tǒng)層面獲取的卓鹿,在重置手機系統(tǒng)的時候都會被清除菱魔,在系統(tǒng)升級、卸載重裝吟孙、備份恢復都可以保留澜倦,現(xiàn)在本人尚未發(fā)現(xiàn)可以使用嚴格意義上的唯一標識聚蝶。接下來我想跟大家探討的是如何通過“合法”的手段來盡量拿到不會輕易發(fā)生變化的“唯一標識”。
3.** **在2013年3月21日蘋果已經通知開發(fā)者藻治,從2013年5月1日起碘勉,訪問UIDID的應用將不再能通過審核,替代的方案是開發(fā)者應該使用“在iOS?6中介紹的Vendor或Advertising標示符”桩卵。
4.MAC地址不能再用來設別設備
** **還有一個生成iOS設備唯一標示符的方法是使用iOS設備的Media Access Control(MAC)地址验靡。一個MAC地址是一個唯一的號碼,它是物理網絡層級方面分配給網絡適配器的雏节。這個地址蘋果還有其他的名字胜嗓,比如說是硬件地址(Hardware Address)或是Wifi地址,都是指同樣的東西钩乍。
有很多工程和框架都使用這個方法來生成唯一的設備ID辞州。比如說ODIN。然而件蚕,蘋果并不希望有人通過MAC地址來分辨用戶孙技,所以如果你在iOS7系統(tǒng)上查詢MAC地址产禾,它現(xiàn)在只會返回02:00:00:00:00:00排作。
5 ** ** 講真蘋果這傲嬌的小脾氣什么時候能能改改,不過這樣 對于用戶隱私的保護,確實起到很大作用,而且蘋果也沒有把路堵死,現(xiàn)在蘋果明確的表明你應該使用-[UIDevice identifierForVendor]或是-[ASIdentifierManager advertisingIdentifier]來作為你框架和應用的唯一標示符。坦白的來說亚情,應對這些變化也不是那么的難妄痪,見以下代碼片段:
:
NSString*identifierForVendor=[[UIDevice currentDevice].identifierForVendor UUIDString];NSString*identifierForAdvertising=[[ASIdentifierManager sharedManager].advertisingIdentifier UUIDString];
但是這樣用法有個缺點,就是程序每次被刪除以后,獲取到的都是新的uuid,并不能實現(xiàn)每個手機的唯一性.
所以下面要講的就是這個問題的解決辦法.
** **對于這個問題有一個可行的辦法,就是把獲取到的uuid保存在鑰匙串里面,這樣即使程序刪除重裝,獲取到的uuid一直是同一個,實現(xiàn)了手機的唯一標識的作用.
保存鑰匙串 我們需要用到keychain,除此之外,Code Signing Entitlements的創(chuàng)建方法也不夠嚴謹楞件。下面教大家一種方法,簡單快速.
1.新建一個工程衫生,看一下自己的Bundle Id.這個Bundle Id 要和你用真機測試時的證書上面的Bundle Id相匹配。
注意這個Bundle identifier
2.Target - Capabilities - Keychain Sharing - ON
注意圓圈里面的保持一致
這步主要目的是打開Keychain Sharing,將它由灰色狀態(tài)的OFF改為藍色狀態(tài)的ON土浸。
會自動生成這個
左側的目錄會自動生成Entitlements文件罪针,不需要自己創(chuàng)建了。
也就是說黄伊,Bundle Identifier泪酱、Keychain Sharing的Keychain Groups、Entitlements文件的Keychain Access Groups的第一個元素还最,它們要保持上圖所示的一致性墓阀。
設置好了以后可以運行下程序,沒問題可以進行下一步拓轻。
.傳說中的uuid類和keychain類來啦
既然蘋果的keychain方法會崩潰而且有些復雜斯撮,我們只保存一個uuid的話可以用下面的簡單方法:
KeyChainStore.h#import@interfaceKeyChainStore:NSObject+(void)save:(NSString*)service data:(id)data;+(id)load:(NSString*)service;+(void)deleteKeyData:(NSString*)service;@end
KeyChainStore.m////? KeyChainStore.m//? getUUID////? Created by ckl@pmm on 16/9/18.//? Copyright ? 2016年 CKLPronetway. All rights reserved.//#import"KeyChainStore.h"@implementationKeyChainStore+(NSMutableDictionary*)getKeychainQuery:(NSString*)service{return[NSMutableDictionary dictionaryWithObjectsAndKeys:(id)kSecClassGenericPassword,(id)kSecClass,service,(id)kSecAttrService,service,(id)kSecAttrAccount,(id)kSecAttrAccessibleAfterFirstUnlock,(id)kSecAttrAccessible,nil];}+(void)save:(NSString*)service data:(id)data{//Get search dictionaryNSMutableDictionary*keychainQuery=[selfgetKeychainQuery:service];//Delete old item before add new itemSecItemDelete((CFDictionaryRef)keychainQuery);//Add new object to search dictionary(Attention:the data format)[keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:data]forKey:(id)kSecValueData];//Add item to keychain with the search dictionarySecItemAdd((CFDictionaryRef)keychainQuery,NULL);}+(id)load:(NSString*)service{id ret=nil;NSMutableDictionary*keychainQuery=[selfgetKeychainQuery:service];//Configure the search setting//Since in our simple case we are expecting only a single attribute to be returned (the password) we can set the attribute kSecReturnData to kCFBooleanTrue[keychainQuery setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];[keychainQuery setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];CFDataRef keyData=NULL;if(SecItemCopyMatching((CFDictionaryRef)keychainQuery,(CFTypeRef*)&keyData)==noErr){@try{ret=[NSKeyedUnarchiver unarchiveObjectWithData:(__bridge NSData*)keyData];}@catch(NSException*e){NSLog(@"Unarchive of %@ failed: %@",service,e);}@finally{}}if(keyData)CFRelease(keyData);returnret;}+(void)deleteKeyData:(NSString*)service{NSMutableDictionary*keychainQuery=[selfgetKeychainQuery:service];SecItemDelete((CFDictionaryRef)keychainQuery);}@end
getUUID.h#import@interfacegetUUID:NSObject+(NSString*)getUUID;@end
getUUID.m////? getUUID.m//? getUUID////? Created by ckl@pmm on 16/9/18.//? Copyright ? 2016年 CKLPronetway. All rights reserved.//#import"getUUID.h"#import"KeyChainStore.h"#defineKEY_USERNAME_PASSWORD @"com.company.app.usernamepassword"#defineKEY_USERNAME @"com.company.app.username"#defineKEY_PASSWORD @"com.company.app.password"@implementationgetUUID+(NSString*)getUUID{NSString*strUUID=(NSString*)[KeyChainStore load:@"com.company.app.usernamepassword"];//首次執(zhí)行該方法時,uuid為空if([strUUID isEqualToString:@""]||!strUUID){//生成一個uuid的方法CFUUIDRef uuidRef=CFUUIDCreate(kCFAllocatorDefault);strUUID=(NSString*)CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault,uuidRef));//將該uuid保存到keychain[KeyChainStore save:KEY_USERNAME_PASSWORD data:strUUID];}returnstrUUID;}@end
在Viewcontroller里面執(zhí)行如下代碼
#import"ViewController.h"#import"getUUID.h"@interfaceViewController()@end@implementationViewController-(void)viewDidLoad{[superviewDidLoad];NSLog(@" uuid? is? ---> %@",[getUUID getUUID]);// Do any additional setup after loading the view, typically from a nib.}
打印出來類似于以下的長串字符:
獲取到的uuid.png
把程序卸載掉然后重新運行一次,獲取到的還是上次保存的uuid.
不知道手機越獄以后,會不會改變,因為樓主手機版本是最新的9.3.5,身邊還沒相關越獄設備,希望大家可以自行測試一下.并告知樓主.聯(lián)系方式 : QQ :576484150
參考地址:
:http://blog.sina.com.cn/s/blog_5971cdd00102vqgy.html
:http://blog.csdn.net/chy555chy/article/details/51628079
:http://www.reibang.com/p/29572b756d26
寫了一個demo,已經放在gitHub上了,有興趣的同學可以下載下來瞅瞅.