iOS开发之获取并存储设备唯一标识符
所谓唯一标识,顾名思义有两大特点:
- 唯一性:
就是从每一个设备上获取到的标识都具有唯一性
- 标识性:
通过两个设备上的标,能够充分代表两个不同的设备
iOS中能作为唯一标识符的有很多,如下:
-
UDID(Unique Device Identifier) iOS5之后被禁
-
UUID(Universally Unique Identifier)(苹果官方推荐)
(**注:如果用户完全重置系统((设置程序 -> 通用 -> 还原 -> 还原位置与隐私) ,这个广告标示符会重新生成。另外如果用户明确的还原广告(设置程序-> 通用 -> 关于本机 -> 广告 -> 还原广告标示符) ,那么广告标示符也会重新生成。**)
-
MAC Address iOS7被禁
-
OPEN UDID iOS7之后被禁
-
广告标示符(IDFA-identifierForIdentifier) 可以使用,但因为本身性质,必须有广告服务,并且iTunes Connect上传要进行相关设置
(**注:如果用户完全重置系统((设置程序 -> 通用 -> 还原 -> 还原位置与隐私) ,这个广告标示符会重新生成。另外如果用户明确的还原广告(设置程序-> 通用 -> 关于本机 -> 广告 -> 还原广告标示符) ,那么广告标示符也会重新生成。**)
-
Vendor标示符 (IDFV-identifierForVendor)
(**注:如果用户卸载了同一个vendor对应的所有程序,然后在重新安装同一个vendor提供的程序,此时identifierForVendor会被重置**)
-
推送token+bundle_id
获取唯一标识符方法:
方法一: 通过 CFUUID
- 从iOS 2.0开始,CFUUID就已经出现了,它是CoreFoundation包的一部分,具有C语言的风格
-
创建一个新的 UUID,参数一般传 nil
CFUUIDRef CFUUIDCreate(CFAllocatorRef alloc);
-
将 UUID 转换为 CFStringRef 类型
CFStringRef CFUUIDCreateString(CFAllocatorRef alloc, CFUUIDRef uuid);
-
- (NSString *)createUUID{
CFUUIDRef UUID = CFUUIDCreate(nil);
CFStringRef UUIDString = CFUUIDCreateString(nil, puuid);
NSString * result = (NSString *)CFBridgingRelease(CFStringCreateCopy( NULL, uuidString));
CFRelease(puuid);
CFRelease(uuidString);
return result;
}
方法二: 通过 NSUUID
- NSUUID 在 iOS6 中才出现,这根 CFUUID 几乎完全一样,只不过是 OC 接口
- 创建 NSUUID
+(instancetype)UUID;
- 将转换为字符串
@property (readonly, copy) NSString *UUIDString;
- 创建 NSUUID
+ (NSString *)createUUID{
NSString *uuid = [NSUUID UUID].UUIDString;
return uuid;
}
方法三: 通过 Vindor标示符 (IDFV-identifierForVendor)
- 这种叫法也是在 iOS 6 中新增的,不过获取这个 IDFV 的新方法被添加在已有的 UIDevice 类中。跟 advertisingIdentifier 一样,该方法返回的是一个 NSUUID 对象
- Vindor标识符
@property(nullable,nonatomic,readonly,strong) NSUUID*identifierForVendor
- Vindor标识符
+ (NSString *)createUUID{
NSUUID * currentDeviceUUID = [UIDevice currentDevice].identifierForVendor;
return currentDeviceUUID.UUIDString;
}
方法四: 通过广告标示符(IDFA-identifierForIdentifier)
-
这是 iOS 6 中另外一个新的方法, advertisingIdentifier 是框架 AdSupport.framework 中的内容,因此在使用的时候需要引入框架,通过调用方法,可以看出 ASIdentifierManager 是一个单例, advertisingIdentifier 方法返回一个 NSUUID 对象
- 单例创建
+(ASIdentifierManager )sharedManager;
- 调用方法,返回NSUUID
@property (nonatomic,readonly) NSUUID advertisingIdentifier;
- 单例创建
+(NSString *)createUUID{
NSString *adId = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
return adId;
}
由于每次调用上述方法,都会创建一个新的UUID,因此要想达到标识设备的作用,不得不需要做一些其他的操作,例如:
- 通过NSUserDefaults存储
但是,当应用卸载重新安装的时候,NSUserDefaults会被重置,因此这种存储方式不是理想的办法
- 通过钥匙串存储
这种方法可以避免因为应用卸载,而造成UUID不唯一的错误,下面主要介绍此方法
通过钥匙串存储方法
方案一: 基于 SSKeychain 的唯一识别码方案
- 如上获取的UUID,基于Git上的一个第三方库SSKeychain,可以将UUID保存在keychain里面,每次调用先检查钥匙串里面有没有,有则使用,没有则写进去,保证其唯一性。
参考代码:
- (NSString *)getNewUniqueIdNum{
NSString *uuidStr = [SSKeychain passwordForService:@"com.test.app1" account:@"user"];
if (!uuidStr || [uuidStr isEqualToString:@""])
{
CFUUIDRef uuidRef = CFUUIDCreate(kCFAllocatorDefault);
uuidStr = (__bridge NSString *)CFUUIDCreateString(kCFAllocatorDefault ,uuidRef);
[SSKeychain setPassword:[NSString stringWithFormat:@"%@", uuidStr] forService:@"com.test.app1"account:@"user"];
}
return uuidStr;
}
不同于上面调用的字符串转换(__bridge NSString *),另一种写法:
- (NSString *)getNewUniqueIdNum{
NSString *identifierNumber = [SSKeychain passwordForService:@"com.test.app1"account:@"user"];
CFUUIDRef uuidRef = CFUUIDCreate(NULL);
assert(uuidRef != NULL);
CFStringRef uuidStrRef = CFUUIDCreateString(NULL, uuidRef);
if (!identifierNumber){
[SSKeychain setPassword: [NSString stringWithFormat:@"%@", uuidStrRef] forService:@"com.test.app1"account:@"user"];
identifierNumber = [SSKeychain passwordForService:@"com.test.app1"account:@"user"];
}
return identifierNumber;
}
方案二: 基于 SAMKeyChains 方案
- 不同于如上SSKeychain,基于一个第三方库SAMKeyChains。SAMKeyChains对苹果安全框架API进行了简单封装,支持对存储在钥匙串中密码、账户进行访问,包括读取、删除和设置。SAMKeyChains使用简单,通过实例代码便可掌握。
参考代码:
+ (NSString *)getDeviceId
{
NSString * currentDeviceUUIDStr = [SAMKeychain passwordForService:@" "account:@"uuid"];
if (currentDeviceUUIDStr == nil || [currentDeviceUUIDStr isEqualToString:@""])
{
NSUUID * currentDeviceUUID =[[UIDevice currentDevice] identifierForVendor];
currentDeviceUUIDStr = [currentDeviceUUID UUIDString];
currentDeviceUUIDStr = [currentDeviceUUIDStr stringByReplacingOccurrencesOfString:@"-" withString:@""];
currentDeviceUUIDStr = [currentDeviceUUIDStr lowercaseString];
[SAMKeychain setPassword: currentDeviceUUIDStr forService:@" "account:@"uuid"];
}
return currentDeviceUUIDStr;
}
SAMKeyChains更多详细用法说明可以查看SAMKeyChains Documentation
推荐参考文献
- https://developer.apple.com/documentation/corefoundation/cfuuid-rci#//apple_ref/c/func/CFUUIDCreate
- http://www.jianshu.com/p/7ad22ca88b83
- http://blog.csdn.net/sir_coding/article/details/68943033
- http://blog.163.com/ray_jun/blog/static/1670536422011102744836300/