IS移动开发总结

iOS 2017 手机唯一标识

2017-03-13  本文已影响43人  brave_wu

1.需求

有时候我们需要标识设备的唯一性,区别设备来实现某些功能或者指定特定的设备,此时设备的唯一标识就派上用场了.

2.遇到的坑

对于上述需求,网上有N多方案,但是大部分都是已经过时的.比较好的也就那么几篇,例如这篇  ,我不想赘言.我简单描述下我使用过程中选择了又抛弃了的两个方案:

广告标示符advertisingIdentifier

这是iOS 6的方法,advertisingIdentifier是框架AdSupport.framework的一部分。ASIdentifierManager单例提供了一个方法advertisingIdentifier,通过调用该方法会返回一个NSUUID实例:

NSString *identifier  =  [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];

广告标示符是由系统存储着的。不过即使这是由系统存储的,但是有几种情况下,会重新生成广告标示符。如果用户完全重置系统((设置-> 通用 -> 还原 -> 还原位置与隐私) ,这个广告标示符会重新生成。另外如果用户明确的还原广告(设置程序->通用 -> 关于本机 -> 广告 -> 还原广告标示符),那么广告标示符也会重新生成。关于广告标示符的还原,有一点需要注意:如果程序在后台运行,此时用户“还原广告标示符”,然后再回到程序中,此时获取广告标示符并不会立即获得还原后的标示符。必须要终止程序,然后再重新启动程序,才能获得还原后的广告标示符。

就算用户不去设置这些东西,有的iPhone广告标识全为0,据说是苹果的一个BUG,至今不知如何解决,所以果断弃掉该方案.

Vindor标示符 identifierForVendor

这个标识和之前那个比较相似,都是苹果推荐使用的唯一标识,获取的方法是:

NSString *idfv = [[[UIDevice currentDevice] identifierForVendor] UUIDString];

这个id其实是和你的app的CFBundleIdentifier相关联的,不论你是把所有CFBundleIdentifier
的app全部卸载还是修改CFBundleIdentifier,他都会改变,特别是卸载,我们怎么可能保证用户不会去卸载你的app呢,所以直接弃掉.

3.解决方案

最后我直接使用第三方的openUDID,但是iOS7之后对剪切板的权限进行了限定,openUDID利用剪切板的方法并不那么灵了,所以我改进了一下,使用keychain来保存openUDID得到的值;

直接上代码:

+ (NSString *)getIdentifierForIPhone

{

NSString *identifier;

//取

NSMutableDictionary *readUserPwd = (NSMutableDictionary *)[XFTools load:KEY_USERNAME_PASSWORD];

if (readUserPwd) {

identifier = [readUserPwd objectForKey:KEY_PASSWORD];

}else{

identifier =  [OpenUDID value];

NSMutableDictionary *usernamepasswordKVPairs = [NSMutableDictionary dictionary];

[usernamepasswordKVPairs setObject:identifier forKey:KEY_PASSWORD];

//存

[Tools save:KEY_USERNAME_PASSWORD data:usernamepasswordKVPairs];

}

return identifier;

}

//存

+ (void)save:(NSString *)service data:(id)data {

//Get search dictionary

NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];

//Delete old item before add new item

SecItemDelete((__bridge CFDictionaryRef)keychainQuery);

//Add new object to search dictionary(Attention:the data format)

[keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(__bridge id)kSecValueData];

//Add item to keychain with the search dictionary

SecItemAdd((__bridge CFDictionaryRef)keychainQuery, NULL);

}

+ (NSMutableDictionary *)getKeychainQuery:(NSString *)service {

return [NSMutableDictionary dictionaryWithObjectsAndKeys:

(__bridge id)kSecClassGenericPassword,(__bridge id)kSecClass,

service, (__bridge id)kSecAttrService,

service, (__bridge id)kSecAttrAccount,

(__bridge id)kSecAttrAccessibleAfterFirstUnlock,(__bridge id)kSecAttrAccessible,

nil];

}

//取

+ (id)load:(NSString *)service {

id ret = nil;

NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];

[keychainQuery setObject:(__bridge id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData];

[keychainQuery setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit];

CFDataRef keyData = NULL;

if (SecItemCopyMatching((__bridge 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)deletekeychain:(NSString *)service {

NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];

SecItemDelete((__bridge CFDictionaryRef)keychainQuery);

}

如果觉得繁琐,可以使用第三方的keychain的操作类库,到目前为止钥匙串保存的信息还是比较值得信赖的.

上一篇下一篇

猜你喜欢

热点阅读