1.已禁用-[UIDevice uniqueIdentifier]
蘋果總是把用戶的隱私看的很重要。-[UIDevice uniqueIdentifier]在iOS5實際在iOS5的時候已經被遺棄了,但是iOS7中已經完全的禁用了它。Xcode5甚至不會允許你編譯包含了指引到-[UIDevice uniqueIdentifier]的app。此外,iOS7之前的使用了-[UIDevice uniqueIdentifier] 的app如果在iOS7上運行,它不會返回設備的UUID,而是會返回一串字符串,以FFFFFFFF開頭,跟著-[UIDevice identifierForVendor]的十六進制值。
2.MAC地址不能再用來設別設備
還有一個生成iOS設備唯一標示符的方法是使用iOS設備的Media Access Control(MAC)地址。一個MAC地址是一個唯一的號碼,它是物理網絡層級方面分配給網絡適配器的。這個地址蘋果還有其他的名字,比如說是硬件地址(Hardware Address)或是Wifi地址,都是指同樣的東西。
有很多工程和框架都使用這個方法來生成唯一的設備ID。比如說ODIN。然而,蘋果并不希望有人通過MAC地址來分辨用戶,所以如果你在iOS7系統上查詢MAC地址,它現在只會返回02:00:00:00:00:00。
3.現在蘋果明確的表明你應該使用-[UIDevice identifierForVendor]或是-[ASIdentifierManager advertisingIdentifier]來作為你框架和應用的唯一標示符。坦白的來說,應對這些變化也不是那么的難,見以下代碼片段:
NSString *identifierForVendor = [[UIDevice currentDevice].identifierForVendor UUIDString];
NSString *identifierForAdvertising = [[ASIdentifierManager sharedManager].advertisingIdentifier UUIDString];
每種方法都適配一種特別的用法:
identifierForVendor對供應商來說是唯一的一個值,也就是說,由同一個公司發行的的app在相同的設備上運行的時候都會有這個相同的標識符。然而,如果用戶刪除了這個供應商的app然后再重新安裝的話,這個標識符就會不一致。
advertisingIdentifier會返回給在這個設備上所有軟件供應商相同的 一個值,所以只能在廣告的時候使用。這個值會因為很多情況而有所變化,比如說用戶初始化設備的時候便會改變。
- 由于identifierForVendor刪除了這個供應商的app然后再重新安裝的話,這個標識符就會不一致,所以要解決這個問題可以把第一次生成的唯一標示符,保存到keyChain(鑰匙串)中,當應用被刪除后keyChain中的數據還在,下次在從keyChain中取就OK了
廢話就講到這了,下面是代碼:
#define KEY_UDID @"KEY_UDID"
#define KEY_IN_KEYCHAIN @"KEY_IN_KEYCHAIN"
#import <Security/Security.h>
#import "APPIdentificationManage.h"
@implementation APPIdentificationManage
singleton_implementation(APPIdentificationManage)
#pragma mark 獲取UUID
/**
*此uuid在相同的一個程序里面-相同的vindor-相同的設備下是不會改變的
*此uuid是唯一的,但應用刪除再重新安裝后會變化,采取的措施是:只獲取一次保存在鑰匙串中,之后就從鑰匙串中獲取
**/
- (NSString *)openUDID
{
NSString *identifierForVendor = [[UIDevice currentDevice].identifierForVendor UUIDString];
return identifierForVendor;
}
#pragma mark 保存UUID到鑰匙串
- (void)saveUDID:(NSString *)udid
{
NSMutableDictionary *udidKVPairs = [NSMutableDictionary dictionary];
[udidKVPairs setObject:udid forKey:KEY_UDID];
[[APPIdentificationManage sharedAPPIdentificationManage] save:KEY_IN_KEYCHAIN data:udidKVPairs];
}
#pragma mark 讀取UUID
/**
*先從內存中獲取uuid,如果沒有再從鑰匙串中獲取,如果還沒有就生成一個新的uuid,并保存到鑰匙串中供以后使用
**/
- (id)readUDID
{
if (_uuid == nil || _uuid.length == 0) {
NSMutableDictionary *udidKVPairs = (NSMutableDictionary *)[[APPIdentificationManage sharedAPPIdentificationManage] load:KEY_IN_KEYCHAIN];
NSString *uuid = [udidKVPairs objectForKey:KEY_UDID];
if (uuid == nil || uuid.length == 0) {
uuid = [self openUDID];
[self saveUDID:uuid];
}
_uuid = uuid;
}
return _uuid;
}
#pragma mark 刪除UUID
- (void)deleteUUID
{
[APPIdentificationManage delete:KEY_IN_KEYCHAIN];
}
#pragma mark 查詢鑰匙串
- (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 nil];
}
#pragma mark 將數據保存到鑰匙串
- (void)save:(NSString *)service data:(id)data {
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
SecItemDelete((__bridge_retained CFDictionaryRef)keychainQuery);
[keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(__bridge_transfer id)kSecValueData];
SecItemAdd((__bridge_retained CFDictionaryRef)keychainQuery, NULL);
}
#pragma mark 加載鑰匙串中數據
- (id)load:(NSString *)service {
id ret = nil;
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
[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;
}
#pragma mark 刪除鑰匙串中數據
- (void)delete:(NSString *)service {
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
SecItemDelete((__bridge_retained CFDictionaryRef)keychainQuery);
}
@end