==== Apple公司于2013年5月1日开始,拒绝采集UDID的App上架App Store
==== iOS 5.0 之后uniqueIdentifier方法就被废弃掉了;
==== iOS 7.0 中苹果又封杀mac地址。
==== iOS 7.0 系统把粘贴板的访问权限限制在了同一个开发者的范围内OpenUDID失效.
所以说,现在想通过代码获取是不可能的了,如果你想看看你设备的UDID,可以通过iTunes来查看。
而且我们需要注意的一点是,对于已越狱了的设备,UDID并不是唯一的.使用Cydia插件UDIDFaker,可以为每一个应用分配不同的UDID.
所以UDID作为标识唯一设备就不要幻想了
目前最终方案:
idfv + keychain
或者
UUID + keychain
唯一缺点:用户抹掉iPhone数据,刷机或重装系统会让该id重置.
一.UDID(Unique Device Identifier)
UDID的全称是Unique Device Identifier,顾名思义,它就是苹果IOS设备的唯一识别码,它由40个字符的字母和数字组成。每台iOS设备的UDID是唯一且永远不会改变。但是Unique Device Identifier 最早就被苹果封杀了,使用后上线会被拒绝。
那么有没有另外的办法来获取用户设备的唯一标识符呢?答案是有的,当然这样的标识符不是苹果隐藏的UDID了,使用OpenUDID开源代码,这个代码通过一些特殊的算法,创建了每一个设备的唯一标识符,你可以拿过来用来识别设备了。
但是.OpenUDID是用系统粘贴板作为中间存储供所有app调用.IOS7系统把粘贴板的访问权限限制在了同一个开发者的范围内,既同一个开发者的多个app在同个设备上共享粘贴板 ,所以OpenUDID已经不可用.
二.MAC Address
使用WiFi的mac地址来取代已经废弃了的uniqueIdentifier方法。具体可见:
然而在iOS 7中苹果再一次无情的封杀mac地址,使用之前的方法获取到的mac地址全部都变成了02:00:00:00:00:00。
三.OPEN UDID
第三方最出名的莫过于此了,但是iOS7这货也无法使用了,由于iOS7对剪贴板(OpenUDID保存到剪贴版中)的限制,导致同一个设备上应用间,无法再共享一个OpenUDID。
OpenUDID原理:安装第一个OpenUDID开发的游戏App1,生成OpenUDID,安装第二款OpenUDID的App2,会从剪贴板中获取出之前生成的UDID,App1、App2都是使用的同一个UDID,但是如果删除所有OpenUDID的游戏后,重新安装App1,这时生成的UDID就已经重置了,和之前的已然不同,玩家的账号信息已丢失,要被投诉了……
当将设备上所有使用了OpenUDID方案的应用程序删除,且设备关机重启,xcode彻底清除并重启,重装应用程序去获取OpenUDID,此时OpenUDID变化,与之前不一样了
四.UUID(Universally Unique Identifier)
UUID是Universally Unique Identifier的缩写,中文意思是通用唯一识别码.
利用UUID获取设备唯一识别码有以下三种方式:
CFUUID从iOS2.0开始,CFUUID就已经出现了。它是CoreFoundatio包的一部分,因此API属于C语言风格。CFUUIDCreate 方法用来创建CFUUIDRef,并且可以获得一个相应的NSString,如下代码:
CFUUIDRef cfuuid = CFUUIDCreate(kCFAllocatorDefault);NSString *cfuuidString = (NSString*)CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, cfuuid));
获得的这个CFUUID值系统并没有存储。每次调用CFUUIDCreate,系统都会返回一个新的唯一标示符。如果你希望存储这个标示符,那么需要自己将其存储到NSUserDefaults, Keychain, Pasteboard或其它地方。
NSUUIDNSUUID在iOS 6中才出现,这跟CFUUID几乎完全一样,只不过它是Objective-C接口。+ (id)UUID 是一个类方法,调用该方法可以获得一个UUID。通过下面的代码可以获得一个UUID字符串:
NSString *uuid = [[NSUUID UUID] UUIDString];
跟CFUUID一样,这个值系统也不会存储,每次调用的时候都会获得一个新的唯一标示符。如果要存储的话,你需要自己存储。在我读取NSUUID时,注意到获取到的这个值跟CFUUID完全一样(不过也可能不一样):
示例: 68753A44-4D6F-1226-9C60-0050E4C00067
1,利用 NSUserDefaults 存取。
这种方法可以保证用户不删除软件情况下的获取同一个数据。因为NSUserDefaults 数据时保存在 沙盒里面的,会随着软件删除而被清空。UUID不好的地方就是用户删除了你开发的程序以后,基本上你就不可能获取之前的数据了。
UUID是基于iOS设备上面某个单个的应用程序,只要用户没有完全删除应用程序,则这个UUID在用户使用该应用程序的时候一直保持不变。如果用户删除了这个应用程序,然后再重新安装,那么这个UUID已经发生了改变。
2,通过调用[[UIDevice currentDevice]identifierForVendor];方法可以获取UUID。
此方法是官方6.0系统后推出的,指定唯一标示符号;不同软件,不同机器,运行的时候值都是不一样的;也就是说可以满足标识唯一性。
但是有一个问题需要注意,大部分正版的手机在软件卸载了、然后重新安装的情况下还是会保持和原先保存同一个值。但是对于有些越狱或者美版的手机 在卸载后仍然会重新获取数据。
3,Keychain方案
KeyChian 是保存在沙盒之外的存储数据,相当于Dictionary, 所有应用都可以获取和保存,因此当一个软件卸载之后完全不影响里面的数据,这样当软件重新安装之后,理所当然的可以获取里面的原数据。
这里我用的还是别人封装过的一些代码SFHFKeychainUtils(可以在网上任意下载):
//保存数据
BOOL s = [SFHFKeychainUtils storeUsername:name andPassword:pswd forServiceName:server updateExisting:NO error:nil];
//获取密码
NSString * psw = [SFHFKeychainUtils getPasswordForUsername:name andServiceName:server error:nil];
五.广告标示符(IDFA-identifierForIdentifier)
这是iOS 6中另外一个新的方法,advertisingIdentifier是新框架AdSupport.framework的一部分。ASIdentifierManager单例提供了一个方法advertisingIdentifier,通过调用该方法会返回一个上面提到的NSUUID实例。
NSString *adId = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
跟CFUUID和NSUUID不一样,广告标示符是由系统存储着的。不过即使这是由系统存储的,但是有几种情况下,会重新生成广告标示符。如果用户完全重置系统((设置程序 -> 通用 -> 还原 -> 还原位置与隐私) ,这个广告标示符会重新生成。另外如果用户明确的还原广告(设置程序-> 通用 -> 关于本机 -> 广告 -> 还原广告标示符) ,那么广告标示符也会重新生成。关于广告标示符的还原,有一点需要注意:如果程序在后台运行,此时用户“还原广告标示符”,然后再回到程序中,此时获取广告标示符并不会立即获得还原后的标示符。必须要终止程序,然后再重新启动程序,才能获得还原后的广告标示符。之所以会这样,我猜测是由于ASIdentifierManager是一个单例。
针对广告标示符用户有一个可控的开关“限制广告跟踪”。Nick Arnott的文章中已经指出了。将这个开关打开,实际上什么也没有做,不过这是希望限制你访问广告标示符。这个开关是一个简单的boolean标志,当将广告标示符发到任意的服务器端时,你最好判断一下这个值,然后再做决定。
示例: 1E2DFA89-496A-47FD-9941-DF1FC4E6484A
苹果推荐的就是使用IDFA、IDFV了,官方推荐那必然妥妥的了
idfa: 适用于对外:例如广告推广,换量等跨应用的用户追踪等
idfv: 适用于对内:例如分析用户在应用内的行为等
idfa:主要用于广告,可能会获取不到,iOS用户也可以 设置|隐私|广告追踪 里重置此id的值,虽然玩家一般不会重置,但是上述理由已经不足以把idfa作为账号了
idfv:顾名思义,是给Vendor标识用户用的,每个设备在所属同一个Vender的应用里,都有相同的值。其中的Vender是指应用提供商,但准确点说,是通过BundleID的DNS反转的前两部分进行匹配,如果相同就是同一个Vender,例如对于com.somecompany.appone,com.somecompany.apptwo 这两个BundleID来说,就属于同一个Vender,共享同一个idfv的值。和idfa不同的是,idfv的值是一定能取到的,所以非常适合于作为内部用户行为分析的主id,来标识用户,替代OpenUDID。
六.Vindor标示符 (IDFV-identifierForVendor)
全名:identifierForVendor
获取代码:
NSString *idfv = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
来源:iOS6.0及以后
说明:顾名思义,是给Vendor标识用户用的,每个设备在所属同一个Vender的应用里,都有相同的值。其中的Vender是指应用提供商,但准确点说,是通过BundleID的反转的前两部分进行匹配,如果相同就是同一个Vender,例如对于com.taobao.app1, com.taobao.app2 这两个BundleID来说,就属于同一个Vender,共享同一个idfv的值。和idfa不同的是,idfv的值是一定能取到的,所以非常适合于作为内部用户行为分析的主id,来标识用户,替代OpenUDID。
注意:如果用户将属于此Vender的所有App卸载,则idfv的值会被重置,即再重装此Vender的App,idfv的值和之前不同。
六.推送送token+bundle_id的方法:
1、应用中增加推送用来获取token
2、获取应用bundle_id
3、根据token+bundle_id进行散列运算
apple push token保证设备唯一,但必须有网络情况下才能工作,该方法不依赖于设备本身,但依赖于apple push,而苹果push有时候会抽风的。
总结
很不幸,上面所有这些表示设备唯一号的标识,在IOS7中要么被禁止使用,要么重新安装程序后两次获取的标识符不一样。
由于IOS系统存储的数据都是在sandBox里面,一旦删除App,sandBox也不复存在。好在有一个例外,那就是keychain(钥匙串)。
通常情况下,IOS系统用NSUserDefaults存储数据信息,但是对于一些私密信息,比如密码、证书等等,就需要使用更为安全的keychain了。
keychain里保存的信息不会因App被删除而丢失。所以,可以利用这个keychain这个特点来保存设备唯一标识。
那么,如何在应用里使用使用keyChain呢,我们需要导入Security.framework ,keychain的操作接口声明在头文件SecItem.h里。
直接使用SecItem.h里方法操作keychain,需要写的代码较为复杂,我们可以使用已经封装好了的工具类KeychainItemWrapper来对keychain进行操作。
KeychainItemWrapper是apple官方例子“GenericKeychain”里一个访问keychain常用操作的封装类,在官网上下载了GenericKeychain项目后,只需要把“KeychainItemWrapper.h”和“KeychainItemWrapper.m”拷贝到我们项目,并导入Security.framework 。KeychainItemWrapper的用法:
/** 初始化一个保存用户帐号的KeychainItemWrapper */
KeychainItemWrapper *wrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@"Account Number"
accessGroup:@"YOUR_APP_ID_HERE.com.yourcompany.AppIdentifier"];
//保存数据
[wrapper setObject:@"<帐号>" forKey:(id)kSecAttrAccount];
[wrapper setObject:@"<帐号密码>" forKey:(id)kSecValueData];
//从keychain里取出帐号密码
NSString *password = [wrapper objectForKey:(id)kSecValueData];
//清空设置
[wrapper resetKeychainItem];
其中方法“- (void)setObject:(id)inObject forKey:(id)key;”里参数“forKey”的值应该是Security.framework 里头文件“SecItem.h”里定义好的key,用其他字符串做key程序会出错!