iOS设备的标识---UUID和IDFA

 

一:1.1 :UDIDcss

简介:UDID的全称是Unique Device Identifier,顾名思义,它就是苹果IOS设备的惟一识别码,它由40个字符的字母和数字组成。在不少须要限制一台设备一个帐号的应用中常常会用到。在iOS5中能够获取到设备的UDID,iOS7中已经彻底的禁用了它。iOS7以前的使用了的app若是在iOS7上运行,它不会返回设备的UDID,而是会返回一串字符串,以FFFFFFFF开头,跟着identifierForVendor的十六进制值。html

获取:[[UIDevice currentDevice] uniqueIdentifier]python

废弃:iOS6ios

1.2 IDFV

简介:iOS 6.0系统新增用于替换uniqueIdentifier的接口。是给Vendor标识用户用的,每一个设备在所属同一个Vender的应用里,都有相同的值。其中的Vender是指应用提供商,但准确点说,是经过BundleID的DNS反转的前两部分进行匹配,若是相同就是同一个Vender,例如对于com.somecompany.appone,com.somecompany.apptwo这两个BundleID来讲,就属于同一个Vender,共享同一个idfv的值。和idfa不一样的是,idfv的值是必定能取到的,因此很是适合于做为内部用户行为分析的主id,来标识用户,替代OpenUDID。若是用户将属于此Vender的全部App卸载,则idfv的值会被重置,即再重装此Vender的App,idfv的值和以前不一样。git


  identifierForVendor对供应商来讲是惟一的一个值,也就是说,由同一个公司发行的的app在相同的设备上运行的时候都会有这个相同的标识符。然而,若是用户删除了这个供应商的app而后再从新安装的话,这个标识符就会不一致。github

获取:[[[UIDevice currentDevice] identifierForVendor] UUIDString]算法

适用:iOS6.0+数组

例子:95955F33-BFBD-48BA-A630-86XXXXXX482Dbash

1.3 IDFA

简介:广告标示符,适用于对外:例如广告推广,换量等跨应用的用户追踪等。但若是用户彻底重置系统((设置程序 -> 通用 -> 还原 -> 还原位置与隐私) ,这个广告标示符会从新生成。另外若是用户明确的还原广告(设置程序-> 通用 -> 关于本机 -> 广告 -> 还原广告标示符) ,那么广告标示符也会从新生成。注意:若是程序在后台运行,此时用户“还原广告标示符”,而后再回到程序中,此时获取广 告标示符并不会当即得到还原后的标示符。必需要终止程序,而后再从新启动程序,才能得到还原后的广告标示符。在同一个设备上的全部App都会取到相同的值,是苹果专门给各广告提供商用来追踪用户而设的,网络

  advertisingIdentifier会返回给在这个设备上全部软件供应商相同的 一个值,因此只能在广告的时候使用。这个值会由于不少状况而有所变化,好比说用户初始化设备的时候便会改,用户关闭 而后打开  也会变

 


用户能够在 设置 -> 隐私 -> 广告追踪 里重置此id的值,或限制此id的使用。

获取:[[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];

适用:iOS6.0+

例子:9C287922-EE26-4501-94B5-DDEXXXXXX475

1.4 MAC地址

简介:MAC地址在网络上用来区分设备的惟一性,接入网络的设备都有一个MAC地址,他们确定都是不一样的,是惟一的。一部iPhone上可能有多个MAC地址,包括WIFI的、SIM的等,可是iTouch和iPad上就有一个WIFI的,所以只需获取WIFI的MAC地址就行了,也就是en0的地址。MAC地址就如同咱们身份证上的身份证号码,具备全球惟一性。但在iOS7以后,若是请求Mac地址都会返回一个固定值。

注意:因为idfa会出现取不到的状况,故毫不能够做为业务分析的主id,来识别用户。 好比开启限制广告追踪
废弃:iOS7.0+

获取:

- (NSString *)macAddress  
{  

    int mib[6]; size_t len; char *buf; unsigned char *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 char *)LLADDR(sdl); NSString *outstring = [NSString stringWithFormat:@"%02x:%02x:%02x:%02x:%02x:%02x", *ptr, *(ptr+1), *(ptr+2), *(ptr+3), *(ptr+4), *(ptr+5)]; NSLog(@"outString:%@", outstring); free(buf); return [outstring uppercaseString]; } 
1.5 KeyChain

简介:iOS整个系统有一个KeyChain,每一个程序均可以往KeyChain中记录数据,并且只能读取到本身程序记录在KeyChain中的数据。并且就算咱们程序删除掉,系统通过升级之后再安装回来,依旧能够获取到与以前一致的UDID(系统还原、刷机除外)。所以咱们能够将UUID的字符串存储到KeyChain中,而后下次直接从KeyChain获取UUID字符串。(本示例中使用KeychainItemWrapper工具类)

获取

+ (NSString *)UUID { KeychainItemWrapper *keyChainWrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@"MYAppID" accessGroup:@"com.test.app"]; NSString *UUID = [keyChainWrapper objectForKey:(__bridge id)kSecValueData]; if (UUID == nil || UUID.length == 0) { UUID = [[[UIDevice currentDevice] identifierForVendor] UUIDString]; [keyChainWrapper setObject:UUID forKey:(__bridge id)kSecValueData]; } return UUID; } 
1.6 AppleAccount

简介:虽然苹果在iOS6中禁用了获取uuid的方式,可是只要你研究下就知道这个API只是私有化了,使用私有API仍是能够获取设备的uuid。可是这个方面也面临着风险:好比API变动以及AppStore审核问题,可是在越狱设备上你仍是能够尽情享用的。

类:AADeviceInfo(dump出头文件)

@class NSObject<OS_dispatch_semaphore>, APSConnection, NSData; @interface AADeviceInfo : NSObject { APSConnection *_apsConnection; BOOL _tokenDone; NSData *_token; NSObject<OS_dispatch_semaphore> *_tokenSema; } + (id)userAgentHeader; + (id)signatureWithDictionary:(id)arg1; + (id)apnsToken; + (id)serialNumber; + (id)clientInfoHeader; + (id)appleIDClientIdentifier; + (id)productVersion; + (id)osVersion; + (id)udid; + (id)infoDictionary; - (id)wifiMacAddress; - (id)regionCode; - (id)deviceClass; - (id)osName; - (id)productType; - (id)apnsToken; - (id)serialNumber; - (id)deviceInfoDictionary; - (id)appleIDClientIdentifier; - (id)productVersion; - (id)osVersion; - (id)udid; - (id)init; - (void).cxx_destruct; - (id)buildVersion; @end 

获取:[AADeviceInfo udid]

使用方法:在项目中将真机上的AppleAccount.framework框架导出,引入Xcode工程中,利用runtime或者直接使用该类就行。
(细节补充:导出AppleAccount.framework后,进入AppleAccount.framework的根目录,新建Headers文件夹,而后将dump出的头文件放在Headers目录,就能够像引用第三方framework同样在项目中使用)

二: iOS10 获取idfa的坑

ios10更新以后一旦开启了 设置->隐私->广告->限制广告跟踪以后 获取到的idfa将会是一串00000 跟mac地址一个尿性,并且每次开启在关闭以后 相应的idfa也会从新生成,至关于还原了一次广告标识符。

 

 

获取idfa的方法:

#import <AdSupport/AdSupport.h> NSString *idfa = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString]; 

ios10 以后最好加一个判断[[ASIdentifierManager sharedManager] isAdvertisingTrackingEnabled] 返回值是BOOL值 若是返回的YES说明没有 “开启限制广告跟踪”,能够获取到正确的idfa 若是返回的是NO,说明等待你的就是一串00000000000

三: IDFA的前世此生

为了保护用户隐私,早在2012年就再也不容许其生态中的玩家获取用户的惟一标识符,可是商家在移动端打广告的时候又但愿能监控到每一次广告投放的效果,所以,苹果想出了折中的办法,就是提供另一套和硬件无关的标识符,用于给商家监测广告效果,同时用户能够在设置里改变这串字符,致使商家没有办法长期跟踪用户行为。这个就叫作广告标识符(IDFA),设置路径是“设置->隐私->广告->还原广告标识符”,以下图所示(iOS9)


 

由于这个IDFA不是惟一的,因此一开始行业内是很抵触的,千方百计去获取UDID(跟手机绑定的,用户不能改变),引发苹果大怒,在13年时禁止全部App获取UDID,不然不能上架,也正由于其生态的封闭性,才能迫使你们就范。虽然IDFA不是惟一的,可是毕竟赛过没有,何况也没有多少用户会去更改。所以,通过几番争斗,IDFA已经成为通用的iPhone用户标识符,这个过程分为6个阶段,我用下图总结


 

然而在今年iOS10推出后,广告界大为震惊,由于苹果推出了“限制广告追踪”功能,设置的路径和iOS9一致。可能细心的人注意到了,这个功能并不是iOS10独有啊,在以前的版本中也同样存在。不过通过实际的测试,在iOS10以前,即便用户打开这个功能,商家同样能够获取IDFA,只不过与以前的不同了,每次切换这个开关与点击“还原广告标识符”的效果同样。而iOS10就不同了,当用户打开这个功能后,商家只能获取到一连串无心义的0,这才是广告界大为震惊的缘由所在


 
四: iOS10 IDFA 获取不到问题解决
4.1 限制广告跟踪背景

中文说明文档

Important

In iOS 10.0 and later, the value of advertisingIdentifier is all zeroes when the user has limited ad tracking. 

也就是说在iOS10上,用户若是开启了 限制广告跟踪 , 获取的idfa将是一串00000000-0000-0000-0000-000000000000

4.2 SimulateIDFA

SimulateIDFA是根据一堆设备信息(每一个app获取的值都是同样的)生成的一个MD5值。用于标志不一样设备。

#######4.2.1 使用:

CoreTelephony.framework
https://github.com/youmi/SimulateIDFA (下载代码) 

在须要获取 SimulateIDFA的地方调用代码:

NSString *simulateIDFA = [SimulateIDFA createSimulateIDFA]; 

simulateIDFA的格式跟IDFA的格式同样

626363D0-90D4-06BF-C281-384E4E69D3E2

#######4.2.2 生成原理
生成的MD5值分两部分。

626363D0-90D4-06BF-C281-384E4E69D3E2 为例:

前16位626363D0-90D4-06BF是由比较稳定的参数组合得到,这前16位只有在系统升级的状况下才会变。

后16位C281-384E4E69D3E2 由 一些比较容易被改变的参数组合生成,比较常见的值变化状况是系统从新启动。

  • 1: 参与前16位计算的参数有:
系统版本(9.3.2)、 硬件信息(N53AP,iPhone6,2,中国移动46002,1048576000)、 coreServices文件建立更新时间(2015-08-07 23:53:00 +0000,2016-06-07 23:53:09 +0000), 系统容量(12266725376) 

这里有一些信息是升级的时候会变的,系统版本、coreServices文件建立更新时间、系统容量

  • 2: 参与后16位计算的参数有:
系统开机时间(1473301191去掉后面的4位数 147330)、
国家代码(CN)、
本地语言(zh-Hans-CN)、
设备名称(XXXX)

这里的参数都是比较容易变化的,系统重启离上次重启有10000秒的话会变,其余参数在设置里面能够修改

4.3 SimulateIDFA与OpenIDFA对比

OpenIDFA 是 Yann Lechelle的一个开源库。同是IDFA的替换方案

#######4.3.1 生成的ID重复的几率对比
假设一个状况。一天内某个国家有10000000(1千万)台相同型号的设备升级到同一个系统。

  • 1: SimulateIDFA
    一天内这个算法可能的值计算, 24x3600(文件建立时间,单位秒)x 10(文件最后修改时间假设偏差在10秒)x 10000000(系统容量偏差范围)x 1000000(设备名称范围,这里假设的是每100台就有2个重复)= 8640000000000000000
    设备a的值为 K,那么设备b的值同为K的可能性为: 1/8640000000000000000. 总共有 10000000台设备。所以,这10000000设备中有与a设备的值同为K的可能性为 1/8640000000000000000 x 10000000 = 1/864000000000。

  • 2: OpenIDFA
    先看一下OpenIDFA的生成算法,OpenIDFA是对下面的参数组合进行MD5.

系统开机时间(1473241127 减去后四位值为 147324)、系统容量(29230571520)、
系统版本(9.3.4)、机型(N78AP,iPod5,1)、国家代码(CN)、本地语言(zh-Hans-CN)、
一些预装的App(因为用的是canOpenURL这个接口,iOS9就已经废了)、时区(Asia/Shanghai)、
当天时间(160804, 16年8月4日,这个值是他天天值都会变化的缘由)

一天内可能的值为系统容量的偏差(10000000)。 ps: 系统启动时间在这种状况下对重复率的下降没起到做用,由于OpenIDFA是减去了系统启动时间的后4位来计算的。同理当天时间也是。

设备a的值为 K,那么设备b的值同为K的可能性为: 1/10000000. 总共有 10000000台设备。所以,这10000000设备中有与a设备的值同为K的可能性为 1/10000000 x 10000000 = 1

#######4.3.2 时效性对比

  • 1: OpenIDFA
    天天获取的值都不同

  • 2: SimulateIDFA
    SimulateIDFA分两部分,前16位是在系统升级的时候才会变化,后16位用户的某些行为可能会致使值变化(例如:重启手机、修改设备名称、修改手机本地语言)

#######4.3.3 总结:
OpenIDFA 有一些限制,生成的IDFA会天天变化,在一些极端条件下重复率比较高。 SimulateIDFA在这方面有更好的表现

五: iOS 提交审核之IDFA的介绍

在咱们提交程序进行审核的时候,最后会有两个选项供咱们选择,一个是Export Compliance(该选项主要是说你的程序设计是否使用了加密,我通常上传的时候都选择No,这个根据你项目实际状况来选择.);另外一个就是Advertising Identifier(广告标示符).

 
 
 

#######5.1 检查是否使用IDFA

检查咱们项目中是否使用广告标示符,其实就是查看咱们-

  • 1: framework中是否有个叫作AdSupport.framework的框架;

  • 2: 若是检查framework没有,多是咱们接入的第三方里面有,用如下方法检查第三方中是否包含有IDFA版本;

(1)打开终端cd到要检查的文件的目录; (2)执行命令:grep -r advertisingIdentifier .(注意别少了点); 

分别对个人项目中和ShareSDK里面进行了检查:


 

#######5.2 各个选项的含义

  • 一、 在 App 内投放广告服务应用中的广告。
    若是你的应用中集成了广告的时候,你须要勾选这一项。

  • 二、 将此 App 安装归因于先前投放的特定广告
    跟踪广告带来的安装。若是你使用了第三方的工具来跟踪广告带来的激活以及一些其余事件,可是应用里并无展现广告你须要勾选这一项。

  • 三、将此 App 中发生的操做归因于先前投放的特定广告
    跟踪广告带来的用户的后续行为。若是你使用了第三方的工具来跟踪广告带来的激活以及一些其余事件。

  • 四、 iOS 中的“限制广告
    跟踪”设置这一项下的内容其实就是对你的应用使用 IDFA 的目的作下确认,只要你选择了采集 IDFA,那么这一项都是须要勾选的。

#######5.3 总结

  • 1: 若是你的应用里只是集成了广告,不追踪广告带来的激活行为,那么选择1和4;

  • 2: 若是你的应用没有广告,而又获取了IDFA。建议选择2和4;

参考资料:
http://www.jianshu.com/p/e222ff751a97
ios10 获取idfa的坑
手机更新到Ios10后获取的idfa全是0?
http://www.cnblogs.com/zxykit/p/5320259.html
iOS10 IDFA 获取不到问题解决
http://www.ithao123.cn/content-8688001.html
http://blog.csdn.net/kaitiren/article/details/52562556
iOS 提交审核之IDFA的介绍

https://www.jianshu.com/p/9d059c17481d
 
还能够看看这俩
https://www.cnblogs.com/isItOk/p/5563200.html
https://www.cnblogs.com/isItOk/p/5875147.html
相关文章
相关标签/搜索