MinggeQingchun 2016-09-07
随着iOS系统版本的演进,或是出于安全的角度,或是出于保护用于隐私的需求,iOS设备获取设备唯一标识的方法也在不断地发生变化。一些公认的设备标识,比如UDID或MAC地址,虽然我们可以通过iTunes等工具查看联机设备的具体参数,但目前已经无法通过合法的系统API获取到这些参数。
因此,如何基于不同iOS版本获取正确的设备标识,对于一个iOS开发人员来讲,是一个特别需要注意的问题。
首先,先对iOS的那一揽子设备标识作一个全面的梳理和总结。
一、UDID
UDID的全称是UniqueDeviceIdentifier,其实从一开始就是苹果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添加到苹果开发者帐号的RegisteredDevices以明确设备的合法性。
二、UUID
UUID全称UniversallyUniqueIdentifier,中文意思是通用唯一识别码.
简单来说,UUID就是一个随机序列字符串生成器,有点像MicrosoftWindows的COMGUID生成器的作用,比起自己随机一个字符串的好处就是这东西能够保证唯一性,适用于标记。
相关的调用方法随着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在iOS6中才出现,这跟CFUUID几乎完全一样,只不过它是Objective-C接口。+(id)UUID是一个类方法,调用该方法可以获得一个UUID。通过下面的代码可以获得一个UUID字符串:
NSString *uuid = [[NSUUID UUID] UUIDString];
需要注意的是,获得的这个CFUUID值系统并没有存储。每次调用CFUUIDCreate,系统都会返回一个新的唯一标示符。如果你希望存储这个标示符,那么需要自己将其存储到NSUserDefaults,Keychain,Pasteboard或其它地方。
从UUID的生成原理,不难看出,UUID最多只能唯一标识自己的应用,涉及到和第三方app的合作,将不再适用。
三、MAC地址(hash值)
毫无疑义,MAC地址是一个唯一的号码,它是物理网络层级方面分配给网络适配器的。这个地址苹果还有其他的名字,比如说是硬件地址(HardwareAddress)或是Wifi地址,都是指同样的东西。
在UDID失效之后,曾经有很多工程和框架都使用这个方法来生成唯一的设备ID。比如说ODIN。
但是,iOS7系统发布之后,苹果明确禁止开发者获取MAC地址。现在,如果你在iOS7和iOS8系统上查询MAC地址,它都返回同样的数值02:00:00:00:00:00。因此这里要特别提醒下iOS7以前就已经开始开发的iOSapp,如果还在用MAC地址,赶紧修改。
四、VindorID
Vindor标示符(IDFV-identifierForVendor)
这种叫法也是在iOS6中新增的,不过获取这个IDFV的新方法被添加在已有的UIDevice类中。调用方法如下:
NSString *idfv = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
苹果官方的文档中对identifierForVendor有如下这样的一段描述:
大概的意识是如果满足这样的条件,那么获取到的这个属性值就不会变:相同的一个程序里面-相同的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也是iOS6中另外一个新的方法,advertisingIdentifier是新框架AdSupport.framework的一部分。ASIdentifierManager单例提供了一个方法advertisingIdentifier,通过调用该方法会返回一个上面提到的NSUUID实例。
NSString *adId = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
但是和UDID以及VindorID不同的是,广告标示符是由系统存储着的。
而且只有在几种极端的场景下,广告标示符的值才会发生变化。
1,用户完全重置系统((设置程序->通用->还原->还原位置与隐私),这个广告标示符会重新生成。
2,用户明确的还原广告(设置程序->通用->关于本机->广告->还原广告标示符),那么广告标示符也会重新生成。后面这一项,在iOS8上已经没有了。
从IDFA的特性不难发现:
六、OpenUDID
前面几个标识,都是通过苹果官方提供的标识方案。而OpenUDID则是一个开源的方案。
在iOS5发布时,uniqueIdentifier被弃用了,这引起了广大开发者需要寻找一个可以替代UDID,并且不受苹果控制的方案。由此OpenUDID成为了当时使用最广泛的开源UDID替代方案。OpenUDID在工程中实现起来非常简单,并且还支持一系列的广告提供商。我们只需引入先关的源码文件,执行下面的调佣即可:
NSString *openUDID = [OpenUDID value];
简单来说,OpenUDID利用了一个非常巧妙的方法在不同程序间存储标示符—在粘贴板中用了一个特殊的名称来存储标示符。通过这种方法,别的程序(同样使用了OpenUDID)知道去什么地方获取已经生成的标示符(而不用再生成一个新的)。只要用户设备上有一个使用了OpenUDID的应用存在时,其他后续安装的应用如果获取OpenUDID,都将会获得第一个应用生成的那个。
但是根据贡献者的代码和方法,和一些开发者的经验,如果把使用了OpenUDID方案的应用全部都删除,再重新获取OpenUDID,此时的OpenUDID就跟以前的不一样。
总结:
不同设备标识符方法的iOS版本适用性
适用性iOS4iOS5iOS6iOS7iOS8iOS9UDID支持过期过期过期过期过期UUID支持支持支持支持支持支持MAC地址支持支持支持过期过期过期VindorID不支持不支持支持支持支持支持IDFA不支持不支持支持支持支持支持OpenUDID支持支持支持支持支持支持
不同设备标识符方法的持久性
是否保持不变app重启app卸载重装系统重启还原位置与隐私系统重置UDID是是是是是UUID否否否否否MAC地址是是是是是VindorID是否(最后一个vendor)是是否IDFA是是是否否OpenUDID是否(最后一个支持OpenUDID)是是否
设备标识符的作用
应用级别,统计应用的活跃量、应用特性参数等app特有的信息。
设备级别,比如统计广告的推广效果,自己的应用必须和第三方推广商使用一致的设备标识。
推荐客户端的使用的iOS标识策略
经常性的和多盟等第三方广告公司合作,设备唯一标识应该遵循业内的通用做法。
推荐使用IDFA作为设备标识。