项目总结三:(设备唯一标识符)IDFV+KeyChain的使用

在最近的项目升级中遇到了一个问题,以前项目获取设备的唯一标识符使用如下方法,

NSString *idfv = [[[UIDevice currentDevice] identifierForVendor] UUIDString]。

以往的升级中可以根据判断是不是一个idfv来识别是不是同一个设备,但这次更新之后,idfv发生了变化,查资料说是卸载之后idfv会改变,但我仅仅是更新应用,一直不明白为什么改变。

苹果的唯一标识符

引用地址:http://my.oschina.NET/hejunbinlan/blog/496724?p=1

在2013年3月21日苹果已经通知开发者,从2013年5月1日起,访问UDID的应用将不再能通过审核,替代的方案是开发者应该使用“在iOS6中介绍的Vendor或Advertising标示符”。unique Identifier即将退出,苹果给了我们Vendor和Advertising identifier两个选择,但应该用哪一个?文档并没有给出确切答案,具体使用哪个完全由你根据自己app的目的来决定。下面我将列出iOS中目前支持的,以及被废弃的唯一标示符方法,并对其做出相应的解释,希望可以帮你做出正确的确定。

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(粘贴板,iOS7之后不共享了,每个应用一个)或其它地方。

NSUUID 

NSUUID在iOS 6中才出现,这跟CFUUID几乎完全一样,只不过它是Objective-C接口。+ (id)UUID 是一个类方法,调用该方法可以获得一个UUID。通过下面的代码可以获得一个UUID字符串:

NSString *uuid = [[NSUUID UUID] UUIDString];

跟CFUUID一样,这个值系统也不会存储,每次调用的时候都会获得一个新的唯一标示符。如果要存储的话,你需要自己存储。在我读取NSUUID时,注意到获取到的这个值跟CFUUID完全一样(不过也可能不一样):

示例:68753A44-4D6F-1226-9C60-0050E4C00067

广告标示符(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

Vindor标示符 (IDFV-identifierForVendor)

这种叫法也是在iOS 6中新增的,不过获取这个IDFV的新方法被添加在已有的UIDevice类中。跟advertisingIdentifier一样,该方法返回的是一个NSUUID对象。

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是什么”。我首先想到的是苹果开发者账号。但事实证明这是错误的。接着我想可能是有一个AppIdentifierPrefix东西,跟钥匙串访问一样,可以在多个程序间共享。同样,这个想法也是的。最后证明,vendor非常简单:一个Vendor是CFBundleIdentifier(反转DNS格式)的前两部分。例如,com.doubleencore.app1 和 com.doubleencore.app2 得到的identifierForVendor是相同的,因为它们的CFBundleIdentifier 前两部分是相同的。不过这样获得的identifierForVendor则完全不同:com.massivelyoverrated 或 net.doubleencore。

在这里,还需要注意的一点就是:如果用户卸载了同一个vendor对应的所有程序,然后在重新安装同一个vendor提供的程序,此时identifierForVendor会被重置。

示例:599F9C00-92DC-4B5C-9464-7971F01F8370

UDID(弃用)

在之前的版本中是可用的,但是在iOS5以及之后的版本中,以及被弃用了。虽然,这个UDID用得很广泛,但是,不得不说的是,它在慢慢的远离开发者,不能在考虑使用UDID了。至于这个标示符是转为私有方法,或者完全从以后的iOS版本中移除,还有待观察。不过,这个UDID在部署企业级签名程序时,非常方便。获取UDID的方法如下:

NSString *udid = [[UIDevice currentDevice] uniqueIdentifier];

示例:bb4d786633053a0b9c0da20d54ea7e38e8776da4

OpenUDID(iOS 7之后不能用)

在iOS 5发布时,uniqueIdentifier被弃用了,这引起了广大开发者需要寻找一个可以替代UDID,并且不受苹果控制的方案。由此OpenUDID成为了当时使用最广泛的开源UDID替代方案。OpenUDID在工程中实现起来非常简单,并且还支持一系列的广告提供商。

NSString *openUDID = [OpenUDID value];

OpenUDID利用了一个非常巧妙的方法在不同程序间存储标示符 — 在粘贴板中用了一个特殊的名称来存储标示符。通过这种方法,别的程序(同样使用了OpenUDID)知道去什么地方获取已经生成的标示符(而不用再生成一个新的)。

之前已经提到过,在将来,苹果将开始强制使用advertisingIdentifier 或identifierForVendor。如果这一天到来的话,即使OpenUDID看起来是非常不错的选择,但是你可能不得不过渡到苹果推出的方法。

示例:0d943976b24c85900c764dd9f75ce054dc5986ff

目前使用的设备唯一标识符办法——IDFV+keychain

将信息保存在系统,这些信息不随app的卸载重装而消失。也不随系统更新而消失,但是如果系统恢复出厂设置,将重新获取

先简单介绍下IDFV的导入方法:使用的时候直接调用就可以

********************.h文件****************

@interfacekeyChain :NSObject

+ (NSString*)getIDFV;

@end


******************.m文件*********************

+ (NSString*)getIDFV{

//定义存入keychain中的账号也就是一个标识表示是某个app存储的内容bundle id就好

NSString*constKEY_USERNAME_PASSWORD =@"com.rails.usernamepassword";

NSString*constKEY_PASSWORD =@"com.rails.password";

//读取账号中保存的内容

NSMutableDictionary*readUserPwd = (NSMutableDictionary*)[keyChainload:KEY_USERNAME_PASSWORD];

//NSLog(@"keychain------><>%@",readUserPwd);

if(!readUserPwd) {

//如果为空说明是第一次安装做存储操作

NSString*identifierStr = [[[UIDevicecurrentDevice]identifierForVendor]UUIDString];

//NSLog(@"identifierStr-----><>%@",identifierStr);

NSMutableDictionary*usernamepasswordKVPairs = [NSMutableDictionarydictionaryWithObject:identifierStrforKey:KEY_PASSWORD];

[keyChainsave:KEY_USERNAME_PASSWORDdata:usernamepasswordKVPairs];

returnidentifierStr;

}else{

return[readUserPwdobjectForKey:KEY_PASSWORD];

}

}

//储存

+ (void)save:(NSString*)service data:(id)data {

//Get search dictionary

NSMutableDictionary*keychainQuery = [selfgetKeychainQuery:service];

//Delete old item before add new item

SecItemDelete((__bridgeCFDictionaryRef)keychainQuery);

//Add new object to search dictionary(Attention:the data format)

[keychainQuerysetObject:[NSKeyedArchiverarchivedDataWithRootObject:data]forKey:(__bridgeid)kSecValueData];

//Add item to keychain with the search dictionary

SecItemAdd((__bridgeCFDictionaryRef)keychainQuery,NULL);

}

+ (NSMutableDictionary*)getKeychainQuery:(NSString*)service {

return[NSMutableDictionarydictionaryWithObjectsAndKeys:

(__bridgeid)kSecClassGenericPassword,(__bridgeid)kSecClass,

service, (__bridgeid)kSecAttrService,

service, (__bridgeid)kSecAttrAccount,

(__bridgeid)kSecAttrAccessibleAfterFirstUnlock,(__bridgeid)kSecAttrAccessible,

nil];

}


+ (id)load:(NSString*)service {

id ret =nil;

NSMutableDictionary*keychainQuery = [selfgetKeychainQuery:service];

//Configure the search setting

//Since in our simple case we are expecting only a single attribute to be returned (the password) we can set the attribute kSecReturnData to kCFBooleanTrue

[keychainQuerysetObject:(__bridgeid)kCFBooleanTrueforKey:(__bridgeid)kSecReturnData];

[keychainQuerysetObject:(__bridgeid)kSecMatchLimitOneforKey:(__bridgeid)kSecMatchLimit];

CFDataRefkeyData =NULL;

if(SecItemCopyMatching((__bridgeCFDictionaryRef)keychainQuery, (CFTypeRef*)&keyData) ==noErr) {

@try{

ret = [NSKeyedUnarchive runarchiveObjectWithData:(__bridgeNSData*)keyData];

}@catch(NSException *e) {

NSLog(@"Unarchive of %@ failed: %@", service, e);

}@finally{

}}

if(keyData)

CFRelease(keyData);

return ret;

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容