因此,如何基于不同iOS版本获取正确的设备标识,对于一个iOS开发人员来讲,是一个特别需要注意的问题。
首先,先对iOS的那一揽子设备标识作一个全面的梳理和总结。
一、UDID
UDID的全称是Unique Device Identifier,其实从一开始就是苹果IOS设备的唯一识别码。UDID由40个字符的字母和数字组成。
iOS5.0以前,系统为我们提供了下面的API获取UDID:
[UIDevice currentDevice] uniqueIdentifier]
但是,从IOS5.0(2011年8月份)开始,苹果宣布将不再支持用uniqueIdentifier方法获取设备的UDID。进一步苹果官方于2013年3月21日苹果已经通知开发者:从2013年5月1日起,访问UIDID的程序将不再被审核通过。也就是说,即使iOS5.0以下的版本,只要iOS使用了相关的API,那么该应用程序基本无法审核通过。
目前,UDID可以通过将iOS设备连接电脑,从iTunes中看到。主要用于在开发、测试过程中,需要将iOS设备的udid添加到苹果开发者帐号的Registered Devices以明确设备的合法性。
二、UUID
UUID全称Universally Unique Identifier,中文意思是通用唯一识别码.
简单来说,UUID就是一个随机序列字符串生成器,有点像Microsoft Windows的COM GUID生成器的作用,比起自己随机一个字符串的好处就是这东西能够保证唯一性,适用于标记。
相关的调用方法随着iOS系统的不同,分为两种:
1,CFUUID
从iOS2.0开始,CFUUID就已经出现了。它是CoreFoundatio包的一部分,因此API属于C语言风格。CFUUIDCreate 方法用来创建CFUUIDRef,并且可以获得一个相应的NSString。具体实现,如下代码:
CFUUIDRef cfuuid = CFUUIDCreate(kCFAllocatorDefault); NSString *cfuuidString (NSString*)CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, cfuuid));
2,NSUUID
NSUUID在iOS 6中才出现,这跟CFUUID几乎完全一样,只不过它是Objective-C接口。+ (id)UUID 是一个类方法,调用该方法可以获得一个UUID。通过下面的代码可以获得一个UUID字符串:
NSString *uuid = [[NSUUID UUID] UUIDString];
需要注意的是,获得的这个CFUUID值系统并没有存储。每次调用CFUUIDCreate,系统都会返回一个新的唯一标示符。如果你希望存储这个标示符,那么需要自己将其存储到NSUserDefaults, Keychain, Pasteboard或其它地方。
从UUID的生成原理,不难看出,UUID最多只能唯一标识自己的应用,涉及到和第三方app的合作,将不再适用。
三、MAC地址(hash值)
毫无疑义,MAC地址是一个唯一的号码,它是物理网络层级方面分配给网络适配器的。这个地址苹果还有其他的名字,比如说是硬件地址(Hardware Address)或是Wifi地址,都是指同样的东西。
在UDID失效之后,曾经有很多工程和框架都使用这个方法来生成唯一的设备ID。比如说ODIN。
但是,iOS7系统发布之后,苹果明确禁止开发者获取MAC地址。现在,如果你在iOS7和iOS8系统上查询MAC地址,它都返回同样的数值02:00:00:00:00:00。因此这里要特别提醒下iOS7以前就已经开始开发的iOS app,如果还在用MAC地址,赶紧修改。
四、Vindor ID
Vindor标示符 (IDFV-identifierForVendor)
这种叫法也是在iOS 6中新增的,不过获取这个IDFV的新方法被添加在已有的UIDevice类中。调用方法如下:
NSString *idfv = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
苹果官方的文档中对identifierForVendor有如下这样的一段描述 :
- The value of this property is the same for apps that come from the same vendor running on the same device. A different value is returned for apps on the same device that come from different vendors, and for apps on different devices regardless of vendor.
大概的意识是如果满足这样的条件,那么获取到的这个属性值就不会变:相同的一个程序里面-相同的vindor-相同的设备。
如果是这样的情况,那么这个值是不会相同的:相同的程序-相同的设备-不同的vindor,或者是相同的程序-不同的设备-无论是否相同的vindor。
一个疑问“vendor是什么”?实际上Vendor就是CFBundleIdentifier(反转DNS格式)的前两部分。例如,com.doubleencore.app1 和 com.doubleencore.app2 得到的identifierForVendor是相同的,因为它们的CFBundleIdentifier 前两部分是相同的。不过这样获得的identifierForVendor则完全不同:com.massivelyoverrated 或 net.doubleencore。
如果用户卸载了同一个vendor对应的所有程序,然后在重新安装同一个vendor提供的程序,此时identifierForVendor会被重置。
五、IDFA
IDFA全称:广告标示符(IDFA-identifierForIdentifier)
IDFA也是iOS 6中另外一个新的方法,advertisingIdentifier是新框架AdSupport.framework的一部分。ASIdentifierManager单例提供了一个方法advertisingIdentifier,通过调用该方法会返回一个上面提到的NSUUID实例。
NSString *adId = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
但是和UDID以及Vindor ID不同的是,广告标示符是由系统存储着的。
而且只有在几种极端的场景下,广告标示符的值才会发生变化。
1,用户完全重置系统((设置程序 ->通用 ->还原 ->还原位置与隐私) ,这个广告标示符会重新生成。
2,用户明确的还原广告(设置程序->通用 ->关于本机 ->广告 ->还原广告标示符) ,那么广告标示符也会重新生成。后面这一项,在iOS8上已经没有了。
从IDFA的特性不难发现:
- IDFA的值是和设备绑定的,也就是说同一台iOS设备上,不同的应用app获取的IDFA的值是一样的。
- 即使用户卸载了某个应用,重新安装应用后,IDFA的值也不会发生改变。
- 只有重置系统才会导致IDFA的改变,但是重置系统首先会抹掉所有APP应用,因此即使改变,也属可接受范畴。
六、Open UDID
前面几个标识,都是通过苹果官方提供的标识方案。而Open UDID则是一个开源的方案。
在iOS 5发布时,uniqueIdentifier被弃用了,这引起了广大开发者需要寻找一个可以替代UDID,并且不受苹果控制的方案。由此OpenUDID成为了当时使用最广泛的开源UDID替代方案。OpenUDID在工程中实现起来非常简单,并且还支持一系列的广告提供商。我们只需引入先关的源码文件,执行下面的调佣即可:
NSString *openUDID = [OpenUDID value];
简单来说,OpenUDID利用了一个非常巧妙的方法在不同程序间存储标示符 — 在粘贴板中用了一个特殊的名称来存储标示符。通过这种方法,别的程序(同样使用了OpenUDID)知道去什么地方获取已经生成的标示符(而不用再生成一个新的)。只要用户设备上有一个使用了OpenUDID的应用存在时,其他后续安装的应用如果获取OpenUDID,都将会获得第一个应用生成的那个。
但是根据贡献者的代码和方法,和一些开发者的经验,如果把使用了OpenUDID方案的应用全部都删除,再重新获取OpenUDID,此时的OpenUDID就跟以前的不一样。
总结:
不同设备标识符方法的iOS版本适用性
适用性 | iOS4 | iOS5 | iOS6 | iOS7 | iOS8 | iOS9 |
UDID | 支持 | 过期 | 过期 | 过期 | 过期 | 过期 |
UUID | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 |
MAC地址 | 支持 | 支持 | 支持 | 过期 | 过期 | 过期 |
Vindor ID | 不支持 | 不支持 | 支持 | 支持 | 支持 | 支持 |
IDFA | 不支持 | 不支持 | 支持 | 支持 | 支持 | 支持 |
OpenUDID | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 |
不同设备标识符方法的持久性
是否保持不变 | app重启 | app卸载重装 | 系统重启 | 还原位置与隐私 | 系统重置 |
UDID | 是 | 是 | 是 | 是 | 是 |
UUID | 否 | 否 | 否 | 否 | 否 |
MAC地址 | 是 | 是 | 是 | 是 | 是 |
Vindor ID | 是 | 否(最后一个vendor) | 是 | 是 | 否 |
IDFA | 是 | 是 | 是 | 否 | 否 |
OpenUDID | 是 | 否(最后一个支持OpenUDID) | 是 | 是 | 否 |
设备标识符的作用
应用级别,统计应用的活跃量、应用特性参数等app特有的信息。
设备级别,比如统计广告的推广效果,自己的应用必须和第三方推广商使用一致的设备标识。
推荐客户端的使用的iOS标识策略
经常性的和多盟等第三方广告公司合作,设备唯一标识应该遵循业内的通用做法。
推荐使用IDFA作为设备标识。