原文链接 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. JS基础1 — 代码要注意的一些问题

    1.在一对  <script></script>  标签中,有错的js代码,那么该错误代码后面的代码不会执行. 例子:<script> alert("He ...

  2. 洛谷P1036 选数

    题目描述 已知 n 个整数 x1,x2,…,xn,以及一个整数 k(k<n).从 n 个整数中任选 k 个整数相加,可分别得到一系列的和.例如当 n=4,k=3,4 个整数分别为 3,7,12, ...

  3. cannot load such file -- bundler/setup解决

    sudo gem install bundler bundle update celluloid

  4. 迅为10.1寸人机界面工业HMI安卓电容屏定制生产供应商

    10.1寸人机界面介绍: 配置铁电存储器:非易失性记忆体,掉电后数据不丢失. 连接云端,支持云服务:数据综合管理,更有效率. 静电防护技术:高强度抗干扰,防静电,防电磁干扰. 提供所有接口的调用源码, ...

  5. java.lang.NoClassDefFoundError: org/w3c/dom/ElementTraversal问题解决

    使用Maven构建项目并加载spring配置文件时,报如下异常 Caused by: java.lang.ClassNotFoundException: org.w3c.dom.ElementTrav ...

  6. CPP-基础:非静态成员函数后面加const,以及mutable修饰成员变量

    非静态成员函数后面加const(加到非成员函数或静态成员后面会产生编译错误),表示成员函数隐含传入的this指针为const指针,决定了在该成员函数中,任意修改它所在的类的成员的操作都是不允许的(因为 ...

  7. iterator方法和for方法 遍历数据库user表结果集ResultSet

    首先,把连接数据库的语句做成工具类,因为会一直用到这几句 代码如下: package com.swift.jdbc; import java.sql.Connection; import java.s ...

  8. C# 使用Epplus导出Excel [3]:合并列连续相同数据

    C# 使用Epplus导出Excel [1]:导出固定列数据 C# 使用Epplus导出Excel [2]:导出动态列数据 C# 使用Epplus导出Excel [3]:合并列连续相同数据 C# 使用 ...

  9. UVa-213-信息解码

    这题的话,我们只要理解题意,应该就不算很难. 我们可以开一个二维数组,用来存放对应的编码字符,第一个下表是length,第二个下标是value,这样一来,我们在读入数据的时候就进行处理,然后想要使用的 ...

  10. (54)zabbix链接及解除模板链接

    上一节就已经涉及到了链接与解除模板链接(link与unlink),这篇文章除了说明怎么链接模板以外,还会特别讲到一些需要特别注意的细节. HOST链接模板之后,便继承了模板里定义的item,trigg ...