原文链接 http://blog.sina.com.cn/s/blog_5971cdd00102vqgy.html

UDID被弃用,使用UUID来作为设备的唯一标识。获取到UUID后,如果用NSUserDefaults存储,当程序被卸载后重装时,再获得的UUID和之前就不同了。使用keychain存储可以保证程序卸载重装时,UUID不变。但当刷机或者升级系统后,UUID还是会改变的。但这仍是目前为止最佳的解决办法了,如果有更好的解决办法,欢迎留言。

(我整理的解决办法的参考来源:http://blog.k-res.net/archives/1081.html)
给大家两个类:
UUID.h中的代码:

#import
尖括号(Foundation/Foundation.h)

@interface UUID : NSObject

+(NSString *)getUUID;

@end

UUID.m中的代码:

#import "UUID.h"

#import 尖括号(Foundation/Foundation.h)

#import
"KeychainItemWrapper.h"

@implementation UUID

+(NSString *)getUUID

{

KeychainItemWrapper *keychainItem = [[KeychainItemWrapper
alloc]

initWithIdentifier:@"UUID"

accessGroup:@"YOUR_BUNDLE_SEED.com.yourcompany.userinfo"];

NSString *strUUID = [keychainItem
objectForKey:(id)CFBridgingRelease(kSecValueData)];

//首次执行该方法时,uuid为空

if ([strUUID isEqualToString:@""])

{

CFUUIDRef uuidRef = CFUUIDCreate(kCFAllocatorDefault);

strUUID = (NSString *)CFBridgingRelease(CFUUIDCreateString (kCFAllocatorDefault,uuidRef));

[keychainItem setObject:strUUID forKey:(id)CFBridgingRelease(kSecValueData)];

}

return strUUID;

}

@end

第二个类是苹果官方的一个Demo里封装的,Demo的下载地址:http://developer.apple.com/library/ios/#samplecode/GenericKeychain/Listings/Classes_KeychainItemWrapper_h.html#//apple_ref/doc/uid/DTS40007797-Classes_KeychainItemWrapper_h-DontLinkElementID_9

KeychainItemWrapper.h中的代码:

#import 尖括号(UIKit/UIKit.h)

@interface KeychainItemWrapper
: NSObject

{

NSMutableDictionary *keychainItemData;

NSMutableDictionary *genericPasswordQuery;

}

@property (nonatomic, retain) NSMutableDictionary *keychainItemData;

@property (nonatomic, retain) NSMutableDictionary
*genericPasswordQuery;

- (id)initWithIdentifier:
(NSString *)identifier
accessGroup:(NSString *)
accessGroup;

- (void)setObject:(id)inObject forKey:(id)key;

- (id)objectForKey:(id)key;

- (void)resetKeychainItem;

@end

KeychainItemWrapper.m中的代码:

#import
"KeychainItemWrapper.h"

#import
尖括号(Security/Security.h)

@interface KeychainItemWrapper
(PrivateMethods)

- (NSMutableDictionary
*)secItemFormatToDictionary:(NSDictionary *)dictionaryToConvert;

- (NSMutableDictionary
*)dictionaryToSecItemFormat:(NSDictionary *)dictionaryToConvert;

-
(void)writeToKeychain;

@end

@implementation
KeychainItemWrapper

@synthesize keychainItemData,
genericPasswordQuery;

- (id)initWithIdentifier:
(NSString *)identifier
accessGroup:(NSString *)
accessGroup;

{

if (self = [super init])

{

genericPasswordQuery = [[NSMutableDictionary alloc]
init];

[genericPasswordQuery setObject:(id)CFBridgingRelease(kSecClassGenericPassword) forKey:(id)kSecClass];

[genericPasswordQuery setObject:identifier forKey:(id)CFBridgingRelease(kSecAttrGeneric)];

if (accessGroup != nil)

{

#if TARGET_IPHONE_SIMULATOR

#else

[genericPasswordQuery setObject:accessGroup forKey:(id)kSecAttrAccessGroup];

#endif

}

[genericPasswordQuery setObject:(id)CFBridgingRelease(kSecMatchLimitOne) forKey:(id)kSecMatchLimit];

[genericPasswordQuery setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnAttributes];

NSDictionary
*tempQuery = [NSDictionary
dictionaryWithDictionary:genericPasswordQuery];

NSMutableDictionary *outDictionary =
nil;

if (!
SecItemCopyMatching((CFDictionaryRef)tempQuery, (CFTypeRef *)&outDictionary) ==
noErr)

{

[self
resetKeychainItem];

[keychainItemData setObject:identifier forKey:(id)kSecAttrGeneric];

if (accessGroup != nil)

{

#if TARGET_IPHONE_SIMULATOR

#else

[keychainItemData setObject:accessGroup forKey:(id)kSecAttrAccessGroup];

#endif

}

}

else

{

self.keychainItemData = [self secItemFormatToDictionary:outDictionary];

}

[outDictionary release];

}

return self;

}

- (void)dealloc

{

[keychainItemData release];

[genericPasswordQuery release];

[super dealloc];

}

- (void)setObject:(id)inObject forKey:(id)key

{

if (inObject == nil) return;

id currentObject = [keychainItemData objectForKey:key];

if (![currentObject isEqual:inObject])

{

[keychainItemData setObject:inObject forKey:key];

[self writeToKeychain];

}

}

- (id)objectForKey:(id)key

{

return [keychainItemData objectForKey:key];

}

- (void)resetKeychainItem

{

OSStatus junk = noErr;

if
(!keychainItemData)

{

self.keychainItemData = [[NSMutableDictionary alloc] init];

}

else
if (keychainItemData)

{

NSMutableDictionary *tempDictionary =
[self dictionaryToSecItemFormat:keychainItemData];

junk = SecItemDelete((CFDictionaryRef)tempDictionary);

NSAssert( junk
== noErr || junk == errSecItemNotFound, @"Problem deleting current dictionary."
);

}

[keychainItemData
setObject:@"" forKey:(id)kSecAttrAccount];

[keychainItemData setObject:@""
forKey:(id)kSecAttrLabel];

[keychainItemData
setObject:@""
forKey:(id)kSecAttrDescription];

[keychainItemData
setObject:@""
forKey:(id)kSecValueData];

}

- (NSMutableDictionary
*)dictionaryToSecItemFormat:(NSDictionary *)dictionaryToConvert

{

NSMutableDictionary *returnDictionary =
[NSMutableDictionary dictionaryWithDictionary:dictionaryToConvert];

[returnDictionary setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];

NSString *passwordString =
[dictionaryToConvert objectForKey:(id)kSecValueData];

[returnDictionary setObject:[passwordString
dataUsingEncoding:NSUTF8StringEncoding] forKey:(id)kSecValueData];

return returnDictionary;

}

- (NSMutableDictionary
*)secItemFormatToDictionary:(NSDictionary *)dictionaryToConvert

{

NSMutableDictionary *returnDictionary =
[NSMutableDictionary
dictionaryWithDictionary:dictionaryToConvert];

[returnDictionary
setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];

[returnDictionary setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];

NSData *passwordData =
NULL;

if (SecItemCopyMatching((CFDictionaryRef)returnDictionary,
(CFTypeRef *)&passwordData)
== noErr)

{

[returnDictionary
removeObjectForKey:(id)kSecReturnData];

NSString *password =
[[[NSString
alloc] initWithBytes:[passwordData
bytes] length:[passwordData
length]

encoding:NSUTF8StringEncoding] autorelease];

[returnDictionary setObject:password forKey:(id)kSecValueData];

}

else

{

NSAssert(NO,
@"Serious error, no matching item found in the
keychain.\n");

}

[passwordData release];

return returnDictionary;

}

- (void)writeToKeychain

{

NSDictionary *attributes = NULL;

NSMutableDictionary *updateItem =
NULL;

OSStatus result;

if (SecItemCopyMatching((CFDictionaryRef)genericPasswordQuery, (CFTypeRef *)&attributes) ==
noErr)

{

updateItem
= [NSMutableDictionary dictionaryWithDictionary:attributes];

[updateItem setObject:[genericPasswordQuery objectForKey:(id)kSecClass] forKey:(id)kSecClass];

NSMutableDictionary *tempCheck =
[self dictionaryToSecItemFormat:keychainItemData];

[tempCheck removeObjectForKey:(id)kSecClass];

#if TARGET_IPHONE_SIMULATOR

[tempCheck removeObjectForKey:(id)kSecAttrAccessGroup];

#endif

result = SecItemUpdate((CFDictionaryRef)updateItem, (CFDictionaryRef)tempCheck);

NSAssert( result == noErr,
@"Couldn't update the Keychain Item." );

}

else

{

result
= SecItemAdd((CFDictionaryRef)[self
dictionaryToSecItemFormat:keychainItemData],
NULL);

NSAssert( result == noErr,
@"Couldn't add the Keychain Item." );

}

}

@end

PS:
1. KeychainItemWrapper类在官方Demo里面也有,如果我复制出错了,大家可以在上面的链接上下载官方Demo.
 
 2.使用时要添加Security.framework
    
 3. 尤为注意的是,UUID类下面这句代码中group的设置方法。
    KeychainItemWrapper
*keychainItem =
[[KeychainItemWrapper
alloc]

initWithIdentifier:@"UUID"

accessGroup:@"YOUR_BUNDLE_SEED.com.yourcompany.userinfo"];

(1)在项目相同的目录下创建KeychainAccessGroups.plist文件。

该文件的结构中最顶层的节点必须是一个名为“keychain-access-groups”的Array,并且该Array中每一项都是一个描述分组的NSString。YOUR_BUNDLE_SEED.com.yourcompany.userinfo就是要设置的组名。
   
   (2)在项目相同的目录下创建KeychainAccessGroups.plist文件。在Target-Build
Settings-Code Signing栏下的Code
Signing Entitlements右侧添加KeychainAccessGroups.plist,如下图。
 
     
  
 到此,工作就完成了。
 
 首次安装程序时,打印出一个uuid,当把程序卸载后,再用getUUID获得
uuid,打印出来的结果和之前相同。证明达到目的。
    测试代码:

NSString * uuid= [UUID getUUID];

NSLog(@"uuid=%@",uuid);

 
测试结果:
  uuid=19AAB430-9CB8-4325-ACC5-D7D386B68960

(绝对有用)iOS获取UUID,并使用keychain存储的更多相关文章

  1. iOS获取UUID

    转自:<iOS获取设备的唯一标识的方法总结以及最好的方法> 参考:<获取iOS设备唯一标识> 总结一下: 1.代码采用CFUUID+KeyChain的实现方式. 2.CFUUI ...

  2. iOS获取UUID,并使用keychain存储

    http://blog.sina.com.cn/s/blog_5971cdd00102vqgy.html

  3. iOS 获取UUID

    -(NSString*)GetUUID { CFUUIDRef puuid = CFUUIDCreate( nil ); CFStringRef uuidString = CFUUIDCreateSt ...

  4. iOS获取设备卸载后不变的UUID

    1.首先导入系统库Security.framework 2.创建文件SFHFKeychainUtils.h如下(复制即可): @interface SFHFKeychainUtils : NSObje ...

  5. iOS获取设备唯一标识的8种方法

    8种iOS获取设备唯一标识的方法,希望对大家有用. UDID UDID(Unique Device Identifier),iOS 设备的唯一识别码,是一个40位十六进制序列(越狱的设备通过某些工具可 ...

  6. iOS获取设备型号、装置类型等信息

    iOS获取设备型号.设备类型等信息 设备标识 关于设备标识,历史上盛行过很多英雄,比如UDID.Mac地址.OpenUDID等,然而他们都陆陆续续倒在了苹果的门下.苹果目前提供了2个方法供App获取设 ...

  7. iOS 获取一个不变的UDID

    原文:iOS7: 如何获取不变的UDID 如何使用KeyChain保存和获取UDID 本文是iOS7系列文章第一篇文章,主要介绍使用KeyChain保存和获取APP数据,解决iOS7上获取不变UDID ...

  8. IOS - UDID IDFA IDFV MAC keychain

    在开发过程中,我们经常会被要求获取每个设备的唯一标示,以便后台做相应的处理.我们来看看有哪些方法来获取设备的唯一标示,然后再分析下这些方法的利弊. 具体可以分为如下几种: UDID IDFA IDFV ...

  9. iOS获取设备唯一标识的各种方法?IDFA、IDFV、UDID分别是什么含义?

    一.UDID (Unique Device Identifier) UDID的全称是Unique Device Identifier,顾名思义,它就是苹果IOS设备的唯一识别码,它由40个字符的字母和 ...

随机推荐

  1. iOS操作系统的层次结构

    iOS操作系统4层结构,如下表 可触摸层 Cocoa Touch layer 媒体层 Media layer 核心服务层 Core Services layer 核心操作系统层 Core OS lay ...

  2. ExpandableListView 安卓二级菜单

    ExpandableListView可以显示一个视图垂直滚动显示两级列表中的条目,这不同于列表视图(ListView).ExpandableListView允许有两个层次:一级列表中有二级列表.比如在 ...

  3. 【排序】插入排序:最稳定:时间复杂度O(n^2)

    想象着自己在玩扑克的时候抓牌,每抓到一张牌,按照从小到大的顺序排序. 如果第二张的点数小于第一张,就交换这两张牌,默认每次抓牌之前,前面的已经排好序了. 再来一张牌,与第二张比较,如果小于第二张,交换 ...

  4. 【进度总结】第一个web应用程序(未完成)

    web程序快速导航 使用Eclipse for Java EE Web Development,并配置Tomcat,这部分内容在众多教程中都描述的十分详细.我直接从代码部分开始记录流程: 这张图是We ...

  5. JAVA的程序基本结构和数据类型

    //源程序 Hello.java public class Hello { static String str ="Hello World"; public static void ...

  6. X和面试随笔

    第一次参加了面试,面试官很好,我写的笔试和回答的都很差劲,虽然技术方面的回答我想抽自己,但是人家还是要了,给了我一个机会,很感谢. 第一道题:设计一个进销存系统的表结构设计 1:老板每天要知道卖出的货 ...

  7. Grid Infrastructure 启动的五大问题 (文档 ID 1526147.1)

    适用于: Oracle Database - Enterprise Edition - 版本 11.2.0.1 和更高版本本文档所含信息适用于所有平台 用途 本文档的目的是总结可能阻止 Grid In ...

  8. db2的离线备份和还原

    db2cmd中运行命令 1.做一个离线备份(db2cmd)mstsc—>db2cmd db2 connect to  mydb                    #连接数据库 db2 lis ...

  9. 许大神- xulinbo xulingbo 分享

    1. 写文章投稿-- 总结的动力 可用性 单次点击 整年年度 流量激增 上下线 双网卡,交换机(网络层面) 稳定性 2. 收藏夹- canssendra 和 oceanBase 练手落地 3. 压测: ...

  10. js 一维数组转成tree 对象

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...