iOS设备唯一标识的前世今生

设备唯一标识


估计很多开发都有被要求过获取一下设备的唯一标识,获取设备的唯一标识经常使用在我们做统计或者是在保证一台设备登录亦或者是做IM的时候可能会考虑去使用它,这一次在自己的需求当中就有一个“账号绑定设备”的需求,这个需求不讨论它的实用性怎样,需求还是需要我们自己去完成。

按照自己的理解针对这个设备的唯一标识,我还是建议少拿!当然说的是针对iOS的设备。下面就来总结一下这个唯一标识的发展过程。

一: UDID


UDID是什么?

你手机连接电脑的时候会弹出iTunes,通过它你就可以直观的看到一台设备的UDID,亦或者大家应该使用过“蒲公英”这个平台,要是你传到这个平台的ipa包要能安装在一台设备上的话就需要你在你自己的开发者证书当中添加到这台设备的UDID,这个过程在你直接使用Xcode连接设备的时候,Xcode也正确添加了开发者证书的前提下也是可以直接在你的开发者当中看到设备的UDID的,这个UDID是苹果给每一台设备配发的唯一标识,要是能直接使用它的话就可以直接解决上面我们的问题,可问题的关键是:

在iOS 5 之后苹果是禁止获取该UDID了,iOS 5有点遥远了。

比如下面是通过iTunes获取到的公司的测试机的设备信息:

iOS设备唯一标识的前世今生

(有同事问我说为什么我iTunes的电话号码下面是序列号,不是UDID啊,你这是个假的iTunes吧!!哈哈....这怎么可能!你点击一下你的系列号试试!我把刀架在同事的脖子上问他是真的还是假的?)

二: IDFA


IDFA又是什么玩意?

IDFA俗称广告ID,估计在开发者里面提交用过应用的朋友都知道,在最后提交的时候苹果会问你有没有使用广告ID,不知道现在该有没有处于懵逼状态不懂苹果为什么要问这个的同行,看了我们这个就会清楚了为什么会这么问呢?这个广告ID就是IDFA。

这个IDFA既然是苹果的玩意,那是不是我们就可以安心的用了,答案肯定是不行的,苹果在用户隐私这一方面还是挺重视的,你肯定是不能用的!在手机设置中你是可以关闭这个按钮的,如下所示:

iOS设备唯一标识的前世今生

通过下面的一样代码你就能拿到这个IDFA:

    #import <AdSupport/AdSupport.h>

    NSString *idfa = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
NSLog(@"拿到的广告ID:%@",idfa);

三:MAC Address 


MAC地址用来表示互联网上每一个站点的标识符,采用十六进制数表示,共六个字节(48位)。其中,前三个字节是由IEEE的注册管理机构 RA负责给不同厂家分配的代码(高位24位),也称为“编制上唯一的标识符” (Organizationally Unique Identifier),后三个字节(低位24位)由各厂家自行指派给生产的适配器接口,称为扩展标识符(唯一性)。

      MAC地址在网络上用来区分设备的唯一性,接入网络的设备都有一个MAC地址,他们肯定都是不同的,是唯一的。一部iPhone上可能有多个MAC地址,包括WIFI的、SIM的等,但是iTouch和iPad上就有一个WIFI的,因此只需获取WIFI的MAC地址就好了,也就是en0的地址。
      MAC地址就如同我们身份证上的身份证号码,具有全球唯一性。这样就可以非常好的标识设备唯一性,听着上面的解释是不是感觉很完美?
      but......
      在iOS 7.0之后你再去请求MAC地址,返回的永远都是同一个值!被禁了......

四:IDFV 也就是UUID


UUID这个你获取起来是比较容易的,但你要是想使用这个作为手机的唯一标识也是不行的,为什么?

我们用事实说明这个问题,下面的这些结论都是经过自己亲自测试的,在现有的版本中是没有问题的!!!

获取UUID,通过下面的方法:

NSString *  strUUID = [UIDevice currentDevice].identifierForVendor.UUIDString

下一步:你把手机的应用删除了再测试一下,我们的结论是:  要是只是单纯的这样获取UUID,删除了应用在重新安装是会发生变化的!

五:UUID + KeyChain


说了上面的这么多,这个就是这篇文章的重点内容了,你获取到UUID之后把UUID存在系统钥匙串中,你看到这个方案肯定也有下面这些疑问:

1、删除了应用这个标识会变吗?

2、升级了系统这个标识会变吗?

3、重置了系统之后呢?这个标识会变吗?

4、既然是使用到了系统钥匙串的东西,那要是我把手机越狱了,会有问题吗?

下面是我们这个方案的总的代码,先把代码给大家,完了我们再通过测试给上面的问题给出答案!给NSString添加一个UUID的类别:

//
// NSString+UUID.h
// Encapsulation
//
// Created by Zhangxu on 2018/1/26.
// 获取UUID并存储到keyChain #import <Foundation/Foundation.h> @interface NSString (UUID) + (NSString *)getUUID; @end @interface KeyChainStore : NSObject // 将UUID保存到钥匙串
+ (void)save:(NSString *)service data:(id)data;
// 读取保存到钥匙串的UUID
+ (id)load:(NSString *)service;
// 删除保存到钥匙串的UUID
+ (void)deleteKeyData:(NSString *)service; @end

上面是.h文件,下面就是 NSString+UUID.m 的文件:

//
// NSString+UUID.m
// Encapsulation
// Created by Zhangxu on 2018/1/26.
// 获取UUID并存储到keyChain #import "NSString+UUID.h" @class KeyChainStore; static NSString * KEY_USERNAME_PASSWORD = @"com.Zhushi.ShuangLongChessAndCard"; @implementation NSString (UUID) +(NSString *)getUUID
{
NSString * strUUID = (NSString *)[KeyChainStore load:KEY_USERNAME_PASSWORD];
//首次执行该方法时,uuid为空
if ([strUUID isEqualToString:@""] || !strUUID)
{
//获取UUID
strUUID = [UIDevice currentDevice].identifierForVendor.UUIDString;
//将该uuid保存到keychain
[KeyChainStore save:KEY_USERNAME_PASSWORD data:strUUID]; //iPhone 8
//11.2版本最初安装应用UUID FD6A5FE3-9EB4-422B-ADD3-17B313B9C8DE
//11.2版本删除重装应用UUID FD6A5FE3-9EB4-422B-ADD3-17B313B9C8DE
//升级系统11.2.5之后UUID FD6A5FE3-9EB4-422B-ADD3-17B313B9C8DE //iPhone 6
//10.3.3最初安装应用UUID 711CAC84-0540-4A44-80B4-26F87D2DD8B7
//10.3.3删除重装应用UUID 711CAC84-0540-4A44-80B4-26F87D2DD8B7
//升级11系统重装应用UUID 711CAC84-0540-4A44-80B4-26F87D2DD8B7
//还原系统之后的应用UUID 1236FB53-CDCE-431E-999B-5C857C679B8A }
return strUUID;
} @end #pragma mark -- KeyChainStore @implementation KeyChainStore + (NSMutableDictionary *)getKeychainQuery:(NSString *)service
{
return [NSMutableDictionary dictionaryWithObjectsAndKeys:
(id)kSecClassGenericPassword,(id)kSecClass,
service, (id)kSecAttrService,
service, (id)kSecAttrAccount,
(id)kSecAttrAccessibleAfterFirstUnlock,(id)kSecAttrAccessible,
nil];
} // 将UUID保存到钥匙串
+ (void)save:(NSString *)service data:(id)data
{
//Get search dictionary
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
//Delete old item before add new item
SecItemDelete((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 dictionary
SecItemAdd((CFDictionaryRef)keychainQuery, NULL);
} // 读取保存到钥匙串的UUID
+ (id)load:(NSString *)service
{
id ret = nil;
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service]; [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);
return ret;
} // 删除保存到钥匙串的UUID
+ (void)deleteKeyData:(NSString *)service
{
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
SecItemDelete((CFDictionaryRef)keychainQuery);
} @end

最后的结论:

通过上面的代码你可以看到整个代码不是多么的复杂,我们关心的还是上面提出的几个疑问,我们一一测试:

1、删除了应用之后这个标识会改变吗?

结论:只要不是越狱的设备,删除应用之后是不会改变这个标识的!(越狱设备后面说!)

2、升级了系统这个标识会变吗?

下面是我把手机升级之后拿到的一组数据,测试机,就不给标识上码了...哈哈

iPhone 8

11.2版本最初安装应用UUID FD6A5FE3-9EB4-422B-ADD3-17B313B9C8DE

11.2版本删除重装应用UUID FD6A5FE3-9EB4-422B-ADD3-17B313B9C8DE

升级系统11.2.5之后UUID     FD6A5FE3-9EB4-422B-ADD3-17B313B9C8DE

iPhone 6

10.3.3最初安装应用UUID   711CAC84-0540-4A44-80B4-26F87D2DD8B7

10.3.3删除重装应用UUID   711CAC84-0540-4A44-80B4-26F87D2DD8B7

升级11系统重装应用UUID   711CAC84-0540-4A44-80B4-26F87D2DD8B7

通过上面的这组数据,我们可以得到的结论是: 在现有版本情况下,升级系统是不会改变这个标识的!

3、用于还原了设备会改变这个标识吗?我们再通过下面这组数据说明,拿我们的iPhone6当小白鼠,要是不知道怎样还原手机设备的看下面的: 设置 -> 通用 ->  还原

iOS设备唯一标识的前世今生

看我们在小白鼠上拿到的数据:

iPhone 6

10.3.3最初安装应用UUID   711CAC84-0540-4A44-80B4-26F87D2DD8B7

10.3.3删除重装应用UUID   711CAC84-0540-4A44-80B4-26F87D2DD8B7

升级11系统重装应用UUID   711CAC84-0540-4A44-80B4-26F87D2DD8B7

还原系统之后的应用UUID   1236FB53-CDCE-431E-999B-5C857C679B8A

结论: 还原系统之后我们这个标识符真的改变了!!!!

4、那要是设备越狱了呢?

结论:在越狱设备上经过测试,是偶尔可以,偶尔不行!!!没错,就是这个结论!

最后:

通过上面的对比数据分析,结果我们也给大家了,通过上面的结论,你就知道了使用 UUID + KeyChain 的利弊,在目前中你想要做唯一标识 UUID + KeyChain 暂时应该是最科学的!

上一篇:codeforces 1285D. Dr. Evil Underscores(字典树)


下一篇:Class获取成员变量信息