iOS 7.0获取iphone UDID 【转】
iOS 7.0
iOS 7中苹果再一次无情的封杀mac地址,使用之前的方法获取到的mac地址全部都变成了02:00:00:00:00:00。有问题总的解决啊,于是四处查资料,终于有了思路是否可以使用KeyChain来保存获取到的唯一标示符呢,这样以后即使APP删了再装回来,也可以从KeyChain中读取回来。有了方向以后就开始做,看关于KeyChain的官方文档,看官方使用KeyChain的Demo,大概花了一下午时间,问题终于解决了。
二、KeyChain介绍
我们搞iOS开发,一定都知道OS X里面的KeyChain(钥匙串),通常要乡镇及调试的话,都得安装证书之类的,这些证书就是保存在KeyChain中,还有我们平时浏览网页记录的账号密码也都是记录在KeyChain中。iOS中的KeyChain相比OS X比较简单,整个系统只有一个KeyChain,每个程序都可以往KeyChain中记录数据,而且只能读取到自己程序记录在KeyChain中的数据。iOS中Security.framework框架提供了四个主要的方法来操作KeyChain:

// 查询
OSStatus SecItemCopyMatching(CFDictionaryRef query, CFTypeRef *result); // 添加
OSStatus SecItemAdd(CFDictionaryRef attributes, CFTypeRef *result); // 更新KeyChain中的Item
OSStatus SecItemUpdate(CFDictionaryRef query, CFDictionaryRef attributesToUpdate); // 删除KeyChain中的Item
OSStatus SecItemDelete(CFDictionaryRef query)

这四个方法参数比较复杂,一旦传错就会导致操作KeyChain失败,这块儿文档中介绍的比较详细,大家可以查查官方文档Keychain Services Reference。
前面提到了每个APP只允许访问自己在KeyChain中记录的数据,那么是不是就没有别的办法访问其他APP存在KeyChain的数据了?
苹果提供了一个方法允许同一个发商的多个APP访问各APP之间的途径,即在调SecItemAdd添加数据的时候指定AccessGroup,即访问组。一个APP可以属于同事属于多个分组,添加KeyChain数据访问组需要做一下两件事情:
a、在APP target的bulibSetting里面设置Code Signing Entitlements,指向包含AceessGroup的分组信息的plist文件。该文件必须和工程文件在同一个目录下,我在添加访问分组的时候就因为plist文件位置问题,操作KeyChain失败,查找这个问题还花了好久的时间。

b、在工程目录下新建一个KeychainAccessGroups.plist文件,该文件的结构中最顶层的节点必须是一个名为“keychain-access-groups”的Array,并且该Array中每一项都是一个描述分组的NSString。对于String的格式也有相应要求,格式为:"AppIdentifier.com.***",其中APPIdentifier就是你的签名中最前面的那部分。

c、在代码中往KeyChain中Add数据的时候,设置kSecAttrAccessGroup,代码如下:

NSString *accessGroup = [NSString stringWithUTF8String:"APPIdentifier.com.cnblogs.smileEvday"];
if (accessGroup != nil)
{
#if TARGET_IPHONE_SIMULATOR
// Ignore the access group if running on the iPhone simulator.
//
// Apps that are built for the simulator aren't signed, so there's no keychain access group
// for the simulator to check. This means that all apps can see all keychain items when run
// on the simulator.
//
// If a SecItem contains an access group attribute, SecItemAdd and SecItemUpdate on the
// simulator will return -25243 (errSecNoAccessForItem).
#else
[dictForQuery setObject:accessGroup forKey:(id)kSecAttrAccessGroup];
#endif
}

这段代码是从官方的Demo中直接拷贝过来的,根据注释我们可以看到,模拟器是不支持AccessGroup的,所以才行了预编译宏来选择性添加。
三、使用KeyChain保存和获取UDID
说了这么多终于进入正题了,如何在iOS 7上面获取到不变的UDID。我们将第二部分所讲的知识直接应用进来就可以了轻松达到我们要的效果了,下面我们先看看往如何将获取到的identifierForVendor添加到KeyChain中的代码。

+ (BOOL)settUDIDToKeyChain:(NSString*)udid
{
NSMutableDictionary *dictForAdd = [[NSMutableDictionary alloc] init]; [dictForAdd setValue:(id)kSecClassGenericPassword forKey:(id)kSecClass];
[dictForAdd setValue:[NSString stringWithUTF8String:kKeychainUDIDItemIdentifier] forKey:kSecAttrDescription]; [dictForAdd setValue:@"UUID" forKey:(id)kSecAttrGeneric]; // Default attributes for keychain item.
[dictForAdd setObject:@"" forKey:(id)kSecAttrAccount];
[dictForAdd setObject:@"" forKey:(id)kSecAttrLabel]; // The keychain access group attribute determines if this item can be shared
// amongst multiple apps whose code signing entitlements contain the same keychain access group.
NSString *accessGroup = [NSString stringWithUTF8String:kKeyChainUDIDAccessGroup];
if (accessGroup != nil)
{
#if TARGET_IPHONE_SIMULATOR
// Ignore the access group if running on the iPhone simulator.
//
// Apps that are built for the simulator aren't signed, so there's no keychain access group
// for the simulator to check. This means that all apps can see all keychain items when run
// on the simulator.
//
// If a SecItem contains an access group attribute, SecItemAdd and SecItemUpdate on the
// simulator will return -25243 (errSecNoAccessForItem).
#else
[dictForAdd setObject:accessGroup forKey:(id)kSecAttrAccessGroup];
#endif
} const char *udidStr = [udid UTF8String];
NSData *keyChainItemValue = [NSData dataWithBytes:udidStr length:strlen(udidStr)];
[dictForAdd setValue:keyChainItemValue forKey:(id)kSecValueData]; OSStatus writeErr = noErr;
if ([SvUDIDTools getUDIDFromKeyChain]) { // there is item in keychain
[SvUDIDTools updateUDIDInKeyChain:udid];
[dictForAdd release];
return YES;
}
else { // add item to keychain
writeErr = SecItemAdd((CFDictionaryRef)dictForAdd, NULL);
if (writeErr != errSecSuccess) {
NSLog(@"Add KeyChain Item Error!!! Error Code:%ld", writeErr); [dictForAdd release];
return NO;
}
else {
NSLog(@"Add KeyChain Item Success!!!");
[dictForAdd release];
return YES;
}
} [dictForAdd release];
return NO;
}

上面代码中,首先构建一个要添加到KeyChain中数据的Dictionary,包含一些基本的KeyChain Item的数据类型,描述,访问分组以及最重要的数据等信息,最后通过调用SecItemAdd方法将我们需要保存的UUID保存到KeyChain中。
获取KeyChain中相应数据的代码如下:

+ (NSString*)getUDIDFromKeyChain
{
NSMutableDictionary *dictForQuery = [[NSMutableDictionary alloc] init];
[dictForQuery setValue:(id)kSecClassGenericPassword forKey:(id)kSecClass]; // set Attr Description for query
[dictForQuery setValue:[NSString stringWithUTF8String:kKeychainUDIDItemIdentifier]
forKey:kSecAttrDescription]; // set Attr Identity for query
NSData *keychainItemID = [NSData dataWithBytes:kKeychainUDIDItemIdentifier
length:strlen(kKeychainUDIDItemIdentifier)];
[dictForQuery setObject:keychainItemID forKey:(id)kSecAttrGeneric]; // The keychain access group attribute determines if this item can be shared
// amongst multiple apps whose code signing entitlements contain the same keychain access group.
NSString *accessGroup = [NSString stringWithUTF8String:kKeyChainUDIDAccessGroup];
if (accessGroup != nil)
{
#if TARGET_IPHONE_SIMULATOR
// Ignore the access group if running on the iPhone simulator.
//
// Apps that are built for the simulator aren't signed, so there's no keychain access group
// for the simulator to check. This means that all apps can see all keychain items when run
// on the simulator.
//
// If a SecItem contains an access group attribute, SecItemAdd and SecItemUpdate on the
// simulator will return -25243 (errSecNoAccessForItem).
#else
[dictForQuery setObject:accessGroup forKey:(id)kSecAttrAccessGroup];
#endif
} [dictForQuery setValue:(id)kCFBooleanTrue forKey:(id)kSecMatchCaseInsensitive];
[dictForQuery setValue:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
[dictForQuery setValue:(id)kCFBooleanTrue forKey:(id)kSecReturnData]; OSStatus queryErr = noErr;
NSData *udidValue = nil;
NSString *udid = nil;
queryErr = SecItemCopyMatching((CFDictionaryRef)dictForQuery, (CFTypeRef*)&udidValue); NSMutableDictionary *dict = nil;
[dictForQuery setValue:(id)kCFBooleanTrue forKey:(id)kSecReturnAttributes];
queryErr = SecItemCopyMatching((CFDictionaryRef)dictForQuery, (CFTypeRef*)&dict); if (queryErr == errSecItemNotFound) {
NSLog(@"KeyChain Item: %@ not found!!!", [NSString stringWithUTF8String:kKeychainUDIDItemIdentifier]);
}
else if (queryErr != errSecSuccess) {
NSLog(@"KeyChain Item query Error!!! Error code:%ld", queryErr);
}
if (queryErr == errSecSuccess) {
NSLog(@"KeyChain Item: %@", udidValue); if (udidValue) {
udid = [NSString stringWithUTF8String:udidValue.bytes];
}
} [dictForQuery release];
return udid;
}

上面代码的流程也差不多一样,首先创建一个Dictionary,其中设置一下查找条件,然后通过SecItemCopyMatching方法获取到我们之前保存到KeyChain中的数据。
四、总结
本文介绍了使用KeyChain实现APP删除后依然可以获取到相同的UDID信息的解决方法。
你可能有疑问,如果系统升级以后,是否仍然可以获取到之前记录的UDID数据?
答案是肯定的,这一点我专门做了测试。就算我们程序删除掉,系统经过升级以后再安装回来,依旧可以获取到与之前一致的UDID。但是当我们把整个系统还原以后是否还能获取到之前记录的UDID,这一点我觉得应该不行,不过手机里面数据太多,没有测试,如果大家有兴趣可以测试一下,验证一下我的猜想。
完整代码地址: https://github.com/smileEvday/SvUDID
大家如果要在真机运行时,需要替换两个地方:
第一个地方是plist文件中的accessGroup中的APPIdentifier。
第二个地方是SvUDIDTools.m中的kKeyChainUDIDAccessGroup的APPIdentity为你所使用的profile的APPIdentifier。
[转自:http://blog.sina.com.cn/s/blog_6f9200bd0101faz4.html]
iOS 7.0获取iphone UDID 【转】的更多相关文章
- iOS 测试之非代码获取 iPhone 型号及其他信息
首先 安装libimobiledevice和ideviceinstaller $ brew uninstall ideviceinstaller $ brew uninstall libimobile ...
- iOS获取iPhone系统等信息和服务器返回空的异常处理
前言: 在项目中经常会遇到需要获取系统的信息来处理一些特殊的需求和服务端返回为空的处理,写在这里只是笔记一下. 获取设备的信息 NSLog(@"globallyUniqueString=%@ ...
- iOS开发之 -- 获取设备的唯一标示符
各种获取设备唯一标识的方法介绍 一.UDID(Unique Device Identifier) UDID的全称是Unique Device Identifier,它就是苹果iOS设备的唯一识别码,它 ...
- iOS 10.0 更新点(开发者视角)
html, body {overflow-x: initial !important;}html { font-size: 14px; } body { margin: 0px; padding: 0 ...
- iOS之iOS11、iPhone X、Xcode9 适配指南
更新iOS11后,发现有些地方需要做适配,整理后按照优先级分为以下三类: 1.单纯升级iOS11后造成的变化: 2.Xcode9 打包后造成的变化: 3.iPhoneX的适配 一.单纯升级iOS11后 ...
- apicloud编译所需的ios证书的获取方法
在我们通过apicloud或hbuilderX这些工具打包ios应用的时候,需要一个ios证书. 那么我们如何生成这个ios证书呢?网上介绍的方法都是需要使用mac电脑,然后用mac电脑的钥匙串访问的 ...
- iOS常用系统信息获取方法
一.手机电量获取,方法二需要导入头文件#import<objc/runtime.h> 方法一.获取电池电量(一般用百分数表示,大家自行处理就好) -(CGFloat)getBatteryQ ...
- (IOS)Swift2.0 Radio 程序分析
本文主要分享下楼主在学习Swift编程过程中,对GitHub上的一个开源项目Swift Radio的研究心得. 项目地址:https://github.com/swiftcodex/Swift-Rad ...
- iOS:关于获取网络类型和运营商信息
目录 1. 获取运营商网络类型 2. 获取运营商信息 返回目录 1. 获取运营商网络类型 Apple的Reachability Sample看起来不错,但是只可以判断是否连接到互联网和是否连接Wifi ...
随机推荐
- JQUERY 拖拽 draggable droppable resizable selectable sortable
今天用了jq ui的拖动碰撞功能,好不容易看到有详细的API解说,记录如下: <script language="JavaScript" type="text/ ...
- HackerRank "Components in a graph"
Regular Union-Find practice one. #include <cmath> #include <cstdio> #include <climits ...
- 剑指offer系列52---约瑟夫环问题
[题目]0,1,...n排成一个圈,从0开始每次删除第m个数,求圆圈最后个数. * [思路]1 用数组模拟圆圈.当数到最后一个数即index==n时,令index==0 重头开始遍历: * 当遇到已经 ...
- 剑指offer系列30-----删除链表中重复的节点
[题目]在一个排序的链表中,存在重复的结点, * 请删除该链表中重复的结点,重复的结点不保留,返回链表头指针. * 例如,链表1->2->3->3->4->4->5 ...
- QT显示中文的几个问题
最近用QT,需要在界面上显示中文,发现QT无法直接在代码中写中文,只能通过曲线救国的方式,比如用QT语言家,QTextCodec的fromloca8bit 研究了半天,终于明白了一些编码的问题 1.V ...
- 重新定位svn地址的方法(windows和linux),svn switch(sw)的帮助信息
今天公司的路由器出现问题,服务器的IP地址也跟着变了,但是原来的svn已经设置好了,现在需要更换地址 查询原地址的方法: root@jack-desktop:codes# svn info 路径: . ...
- 兼容所有浏览器---无缝上下左右交叉运动----原生js+css
<!doctype html> <html> <head> <meta charset="utf-8"> <title> ...
- Orcale(一)概念
一 . 基本概念 : 1. orcaleinstanceclient : orcale的客户端 ( 1. 中文乱码问题 : NLS_LANG=SIMPLIFIED CHINESE_CHINA.Z ...
- ADF_Starting系列5_使用ADF开发富Web应用程序之维护User Interface(Part2)
2014-05-05 Created By BaoXinjian
- apache/php 开启 gzip压缩
1.php方式开启 原理: header("Content-Encoding: gzip"); echo gzencode('songjiankang'); 示例1: functi ...