心境自然凉,凉,凉,37度都要化了。早起顶着太阳走到公司,既能锻炼身体又能思考问题,我不会说太阳烤死了,走一路要40分钟,晒的透透的,吃了个面包,喝了盒酸奶,很营养,这酸奶一定有问题,闹肚子。又能愉快的写代码了,设计没有,后台没更新,我了个擦擦。
方案一
回归正题了,咳咳
iOS6 之前用的Mac地址,但在iOS7以后会返回固定值,方法失效,才有了今天的讨论。
IDFA这是iOS 6中另外一个新的方法,是AdSupport.framework框架中ASIdentifierManager单例提供了一个方法advertisingIdentifier,通过调用该方法会返回一个的NSUUID实例。这一段是我无耻抄的
作为唯一标识符,两个思路。第一,程序生成,但保证每次生成结果一致并唯一。第二,程序每次生成不一致,但由系统存储,不由程序沙盒管理。
IDFA是由系统存储的,并且每次生成都是一致,太好了,可以拿来用。但万物都不可能是完美的,用它需要注意,在往App Store提交时,对于广告选择选择234。再有IDFA 有心的被重置的风险较大,所以这里并不十分推荐,无论如何还是要写上步骤。
1、添加框架
AdSupport.framework
2、添加头文件
#import <AdSupport/ASIdentifierManager.h>
3、使用语句
NSString *adId = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
方案二
UUID+keychain
接下来我摘抄的keychain为钥匙串,用来存储密码和证书的一块加密存储区域,keychain不是存储在手机的沙盒内,而是手机的某个公共区域,手机重启和应用卸载,都不会对这片存储区域造成影响,因为是加密存储不存在被其他应用修改的问题,所以就有人拿keychain来存储唯一标识。
UUID是随机的唯一 数字串,作为设备唯一标识。
两者结合实现了设备唯一标识,当然这个想法是别人想出来的。
直接上步骤快到饭点了
1.Target - Capabilities - Keychain Sharing - ON,左侧的目录会自动生成Entitlements文件,Bundle Identifier、Keychain Sharing的Keychain Groups、Entitlements文件的Keychain Access Groups的第一个元素,它们要保持一致性。
2.上代码 这里是借鉴钟溪妍的,她copy的百度的,百度拿的谁的
UUID.h
#import <Foundation/Foundation.h>
@interface UUID : NSObject
+(NSString *)getUUID;
UUID.m
#import "UUID.h"
#import "KeyChainStore.h"
@implementation UUID
+(NSString *)getUUID
{
NSString * strUUID = (NSString *)[KeyChainStore load:@"com.company.app.usernamepassword"];
//首次执行该方法时,uuid为空
if ([strUUID isEqualToString:@""] || !strUUID)
{
//生成一个uuid的方法
CFUUIDRef uuidRef = CFUUIDCreate(kCFAllocatorDefault);
strUUID = (NSString *)CFBridgingRelease(CFUUIDCreateString (kCFAllocatorDefault,uuidRef));
//将该uuid保存到keychain
[KeyChainStore save:KEY_USERNAME_PASSWORD data:strUUID];
}
return strUUID;
}
@end
KeyChainStore.h
#import <Foundation/Foundation.h>
@interface KeyChainStore : NSObject
+ (void)save:(NSString *)service data:(id)data;
+ (id)load:(NSString *)service;
+ (void)deleteKeyData:(NSString *)service;
@end
KeyChainStore.m
#import "KeyChainStore.h"
@implementation KeyChainStore
+ (NSMutableDictionary *)getKeychainQuery:(NSString *)service {
return [NSMutableDictionary dictionaryWithObjectsAndKeys:
(id)kSecClassGenericPassword,(id)kSecClass,
service, (id)kSecAttrService,
service, (id)kSecAttrAccount,
(id)kSecAttrAccessibleAfterFirstUnlock,(id)kSecAttrAccessible,
nil];
}
+ (void)save:(NSString *)service data:(id)data {
//Get search dictionary
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
//Delete old item before add new item
SecItemDelete((CFDictionaryRef)keychainQuery);
//Add new object to search dictionary(Attention:the data format)
[keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(id)kSecValueData];
//Add item to keychain with the search dictionary
SecItemAdd((CFDictionaryRef)keychainQuery, NULL);
}
+ (id)load:(NSString *)service {
id ret = nil;
NSMutableDictionary *keychainQuery = [self getKeychainQuery: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
[keychainQuery setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
[keychainQuery setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
CFDataRef keyData = NULL;
if (SecItemCopyMatching((CFDictionaryRef)keychainQuery, (CFTypeRef *)&keyData) == noErr) {
@try {
ret = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge NSData *)keyData];
} @catch (NSException *e) {
NSLog(@"Unarchive of %@ failed: %@", service, e);
} @finally {
}
}
if (keyData)
CFRelease(keyData);
return ret;
}
+ (void)deleteKeyData:(NSString *)service {
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
SecItemDelete((CFDictionaryRef)keychainQuery);
}
@end
在PCH中添加如下宏
#ifndef PrefixHeader_pch
#define PrefixHeader_pch
#define KEY_USERNAME_PASSWORD @"com.company.app.usernamepassword"
#define KEY_USERNAME @"com.company.app.username"
#define KEY_PASSWORD @"com.company.app.password"
#endif
最后的最后
NSString * uuid= [UUID getUUID];
NSLog(@"uuid=%@",uuid);
// uuid 你要的在这
相比心静自然凉,倒觉得心凉自然静,更有道理。