今天在app服务端接口文档中看到2个接口名称,是和idfa相关的,就搜索了解了一下,顺便梳理了ios各种设备识别码。

*IDFA英文全称Identifier+for+Advertising,即广告标示符,目前是苹果生态内广告交易的主要标示符,一般和广告商交易一个用户后,广告商会提供用户的idfa作为凭证。主流广告平台腾讯广点通,新浪粉丝通都是基于idfa。

*ios中路径:设置-->>隐私-->>广告-->>还原广告标识符;

每次切换开关按钮和点击[还原广告标识符],在ios10中及之后版本中,商户只能获取到一串毫无意义的0,这样商户就没法对用进行定向投             放了,但是不会减少投放量。用户就会成为没有身份的“黑户”。

【其他】认识一下iOS系统的各种设备识别码

1、UDID----Unique Device Identifier,苹果ios唯一设备识别码,由40个数字字母组合,为了保护用户隐私,苹果已禁读;

2、UUID----Universally Unique IDentifier,是基于苹果设备上某个单个应用程序;只要用户不删除程序,在用户使用期间时候不变的,如果用户删除后再重新安装程序,uuid就会发生变化,缺点是用户删除后程序后再安装,基本不可能拿到之前的数据。

3、MAC地址,用来定义网络设备位置,一个主机有一个MAC地址,MAC地址是网卡决定的,是固定的,为了隐私,苹果已禁读;

4、OpenUDID,不是苹果官方,是第三方用来替代UDID的解决方案,如果全部删除带有OpenUDID SDK包的APP,那么OpenUDID会重新生成,和之前的还不相同,相当于新设备。

5、IDFA 广告标示符,适用于对外:例如广告推广,换量等跨应用的用户追踪等。

6、IDFV,Vindor 标示符 (IDFV-identifierForVendor),来自于同一个开发商(jianshu.App1和jianshu.App2)应用在同一个设备上,此属性值是相同的,不同的运营商运用在同一个设备上值也不同。

那么,这么多设备,我们在互联网的世界,如何标明唯一的一个设备呢?

这个时候就引入了“设备唯一标识符”的概念。

在iOS的生态中,有我们所熟知的IDFA,IDFV,UDID,UUID。

简直能把人整晕。但是他们含义不同,应用场景不同。

为了能在我们的业务中更好的标识一个唯一设备,我们引入OpenUDID类库。

而在介绍这个库之前,我们先来看看,上面说的这几个编码到底有哪里不同,以及如何应用。

一、IDFA

IDFA是一串16进制的32位串。全称是Identifier For Advertising

设计目的是,标识用户设备,用于提供给各个APP之间跟踪广告所用。

例如,你在淘宝里搜索了某个商品之后,你在用浏览器去浏览网页的时候,那个网页的广告就会给你展示相应的那个商品的广告。

IDFA具有以下特性:

  1. IDFA和设备绑定。所有应用在统一设备上获取的都是一样的

  2. iOS10之后,用户可以在设置中关闭限制APP获取

  3. 可以被更改,更改的时机为:

    1. 设置程序 -> 通用 -> 还原 -> 还原位置与隐私

    2. 设置程序-> 通用 -> 关于本机 -> 广告 -> 还原广告标示符

因此,IDFA可以作为一台设备的唯一标识符。但是会有风险被重试,或者被限制获取。

二、IDFV

和IDFA非常类似,是一串16进制的32位串。全称是Identifier For Vendor。

但是不同的是,不仅和设备相关,也和APP提供商相关。

因此,具有以下特性:

  1. IDFV同时和设备,以及APP的提供商相关。一台设备上,同一个APP提供商的IDFV是一样的。

    因此,如果将此台设备上,所有这个APP提供商的APP都删除了,则下一次重新安装此提供商APP时,获取的IDFV会变化。

    例如,Dailyyoga和每日瑜伽获取的IDFV相同,但是把他们两个都删除了,下次重新安装后,IDFV会变化。

所以,IDFV,一般可以用于同一个厂家不同APP之间的在本地的数据互通。

三、UDID

是 iOS 设备的一个唯一识别码,每台 iOS 设备都有一个独一无二的编码,全称是Unique Device Identifier。由40位16进制数组成的字符串。

但是在iOS5以后,已经被苹果禁止通过代码获取了。

目前还有三种方式可以看到UDID:

  1. 使用iTunes,将手机连接电脑

image

  1. 使用XCode->Device,将手机连接电脑

    image

  2. 使用企业级的MDM,制作描述文件,然后搭配网页展示获取UDID。但是只能在Safari中实现。

    目前蒲公英就是这么做的。可以参考蒲公英的获取UDID的地址:https://www.pgyer.com/udid

    具体实现方案,可以参考这里:http://www.skyfox.org/safari-ios-device-udid.html

因此,苹果为了防止厂家滥用,对于客户端来说,目前已经失去了获取UDID的能力。

四、UUID

UUID 是 通用唯一识别码(Universally Unique Identifier)的缩写,是一种软件建构的标准,是国际标准化组织(ISO)提出了一个概念。

它是一个32位的十六进制序列。

其设计目的是,让分布式系统中的所有元素,都能有唯一的辨识信息,而不需要通过中央控制端来做辨识信息的指定。

目前几乎所有系统,windows,Linux,macOS,都已经接入此标准。

UUID是基于当前时间、计数器(counter)和硬件标识(通常为无线网卡的MAC地址)等数据计算生成的。因此,每次重新获取的时候,都不一样,可以在此时此地标识唯一的一台设备。

五、OpenUDID —— 我们使用的识别标识库

UUID目前是比较通用的,在OC中可以通过NSUUID来获取。根据其获取方式,可以知道每次获取都会不同,实际验证也如此。OpenUDID实际也是基于UUID来实现的。

但是我们想要是,一旦我的APP在这台机器上安装上了,无论以后怎么折腾,都获取的是统一个。那么,我们应该如何做呢?

此时,OpenUUID给了我们一个解决策略。(虽然这个库已经很多年没更新过了,API基于系统底层,所以相对稳定。并且我们的APP使用了很长时间,也没有办法替换)

由于DeviceID对于我们的重要性,我们还是有必要仔细阅读一下此库的源码,来弄清楚我们DeviceID的前世今生。接下来,我们一起来看看。

我们首先用流程图来表示一下,OpenUDID的核心功能,以及其数据流向:

YbFEz4.png

  1. OpenUDID中所获取的UDID是使用32位UUID进行了40位的补全而来,它的本质就是一个UUID,是可变的

  2. 为了使其不可变,OpenUDID将此值分别存储在了三个地方

    1. 内存变量 — — 在不退出APP时,可以从内存中直接获取之前的ID

    2. NSUserDefault — — 在不删除APP时,每次打开APP,可以在NSUserDefault中获取到之前生成的ID

    3. UIPasteboard — — 删除了APP后,在同一设备上重新安装APP,依然可以获取到之前已经生成的ID

  3. 不可变是我们要的常态,那么我们需要着重关心的是,这个ID什么时候会变化

    由于最终是存在UIPasteboard的不同域中,而iOS不允许用户主动清理剪贴板,所以变化的时机有以下几个:

    1. 用户抹掉手机中所有数据,出厂还原为一个新手机

    2. 有其他APP,使用API强制清除所有剪贴板(待确认可实施性,但是不排除有这个可能)

    3. 由于OpenUDID是开源,不排除极端情况,有其他恶意APP清除OpenUDID使用的剪贴板域

综上,OpenUDID由于聪明地使用了剪贴板做数据持久化以及共享。所以可以保证只要设备上有APP使用了OpenUDID,就可以生成一个唯一识别码,除非以上情况,此识别码将可以一直持续使用,因此,可靠性很高。

对于我们的业务来说,部分功能使用了DeviceID(即我们通过OpenUDID获取给服务器的ID)进行设备识别的模块,例如,即使用户删除APP,再次安装时,也要在登录页面弹出上次他在这台设备上最后一次登录的账号的功能,可以大胆放心的使用此ID作为识别码。

最后,放上OpenUDID源码。有表述不清或者不对的地方,还请提出质疑。

Let’s think!

//
//  OpenUDID.m
//  openudid
//
//  initiated by Yann Lechelle (cofounder @Appsfire) on 8/28/11.
//  Copyright 2011, 2012 OpenUDID.org
//
//  Initiators/root branches
//      iOS code: https://github.com/ylechelle/OpenUDID
//      Android code: https://github.com/vieux/OpenUDID
//
//  Contributors:
//      https://github.com/ylechelle/OpenUDID/contributors
///*http://en.wikipedia.org/wiki/Zlib_LicenseThis software is provided 'as-is', without any express or impliedwarranty. In no event will the authors be held liable for any damagesarising from the use of this software.Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute itfreely, subject to the following restrictions:1. The origin of this software must not be misrepresented; you must notclaim that you wrote the original software. If you use this softwarein a product, an acknowledgment in the product documentation would beappreciated but is not required.2. Altered source versions must be plainly marked as such, and must not bemisrepresented as being the original software.3. This notice may not be removed or altered from any sourcedistribution.
*/#if __has_feature(objc_arc)
#error This file uses the classic non-ARC retain/release model; hints below... // to selectively compile this file as non-ARC, do as follows:// https://img.skitch.com/20120717-g3ag5h9a6ehkgpmpjiuen3qpwp.png
#endif#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v)  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)#import "OpenUDID.h"
#import <CommonCrypto/CommonDigest.h> // Need to import for CC_MD5 access
#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
#import <UIKit/UIPasteboard.h>
#import <UIKit/UIKit.h>
#else
#import <AppKit/NSPasteboard.h>
#endif#define OpenUDIDLog(fmt, ...)
//#define OpenUDIDLog(fmt, ...) //NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);
//#define OpenUDIDLog(fmt, ...) //NSLog((@"[Line %d] " fmt), __LINE__, ##__VA_ARGS__);static NSString * kOpenUDIDSessionCache = nil;
//static NSString * const kOpenUDIDDescription = @"OpenUDID_with_iOS6_Support";
static NSString * const kOpenUDIDKey = @"OpenUDID";
static NSString * const kOpenUDIDSlotKey = @"OpenUDID_slot";
static NSString * const kOpenUDIDAppUIDKey = @"OpenUDID_appUID";
static NSString * const kOpenUDIDTSKey = @"OpenUDID_createdTS";
static NSString * const kOpenUDIDOOTSKey = @"OpenUDID_optOutTS";
static NSString * const kOpenUDIDDomain = @"org.OpenUDID";
static NSString * const kOpenUDIDSlotPBPrefix = @"org.OpenUDID.slot.";
static int const kOpenUDIDRedundancySlots = 100;@interface OpenUDID (Private)
+ (void) _setDict:(id)dict forPasteboard:(id)pboard;
+ (NSMutableDictionary*) _getDictFromPasteboard:(id)pboard;
+ (NSString*) _generateFreshOpenUDID;
@end@implementation OpenUDID// Archive a NSDictionary inside a pasteboard of a given type
// Convenience method to support iOS & Mac OS X
//
+ (void) _setDict:(id)dict forPasteboard:(id)pboard {
#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR     [pboard setData:[NSKeyedArchiver archivedDataWithRootObject:dict] forPasteboardType:kOpenUDIDDomain];
#else[pboard setData:[NSKeyedArchiver archivedDataWithRootObject:dict] forType:kOpenUDIDDomain];
#endif
}// Retrieve an NSDictionary from a pasteboard of a given type
// Convenience method to support iOS & Mac OS X
//
+ (NSMutableDictionary*) _getDictFromPasteboard:(id)pboard {
#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR id item = nil;if([pboard respondsToSelector:@selector(dataForPasteboardType:)]){item = [pboard dataForPasteboardType:kOpenUDIDDomain];}
#elseid item = [pboard dataForType:kOpenUDIDDomain];
#endif  if (item) {@try{item = [NSKeyedUnarchiver unarchiveObjectWithData:item];} @catch(NSException* e) {OpenUDIDLog(@"Unable to unarchive item %@ on pasteboard!", [pboard name]);item = nil;}}// return an instance of a MutableDictionary return [NSMutableDictionary dictionaryWithDictionary:(item == nil || [item isKindOfClass:[NSDictionary class]]) ? item : nil];
}// Private method to create and return a new OpenUDID
// Theoretically, this function is called once ever per application when calling [OpenUDID value] for the first time.
// After that, the caching/pasteboard/redundancy mechanism inside [OpenUDID value] returns a persistent and cross application OpenUDID
//
+ (NSString*) _generateFreshOpenUDID {NSString* _openUDID = nil;// August 2011: One day, this may no longer be allowed in iOS. When that is, just comment this line out.// March 25th 2012: this day has come, let's remove this "outlawed" call...// August 2012: well, perhaps much ado about nothing; in any case WWDC2012 gave us something to work with; read below
#if TARGET_OS_IPHONE
//    if([UIDevice instancesRespondToSelector:@selector(uniqueIdentifier)]){
//        _openUDID = [[UIDevice currentDevice] uniqueIdentifier];
//    }
#endif//// !!!!! IMPORTANT README !!!!!//// August 2012: iOS 6 introduces new APIs that help us deal with the now deprecated [UIDevice uniqueIdentifier]// Since iOS 6 is still pre-release and under NDA, the following piece of code is meant to produce an error at// compile time. Accredited developers integrating OpenUDID are expected to review the iOS 6 release notes and// documentation, and replace the underscore ______ in the last part of the selector below with the right// selector syntax as described here (make sure to use the right one! last word starts with the letter "A"):// https://developer.apple.com/library/prerelease/ios/#documentation/UIKit/Reference/UIDevice_Class/Reference/UIDevice.html//// The semantic compiler warnings are still normal if you are compiling for iOS 5 only since Xcode will not// know about the two instance methods used on that line; the code running on iOS will work well at run-time.// Either way, it's time that you junped on the iOS 6 bandwagon and start testing your code on iOS 6 ;-)//// WHAT IS THE SIGNIFICANCE OF THIS CHANGE?//// Essentially, this means that OpenUDID will keep on behaving the same way as before for existing users or// new users in iOS 5 and before. For new users on iOS 6 and after, the new official public APIs will take over.// OpenUDID will therefore be obsoleted when iOS reaches significant adoption, anticipated around mid-2013./*September 14; ok, new development. The alleged API has moved!This part of the code will therefore be updated when iOS 6 is actually released.Nevertheless, if you want to go ahead, the code should be pretty easy toguess... it involves including a .h file from a nine-letter framework that endswith the word "Support", and then assigning _openUDID with the only identifier method called on the sharedManager of that new class... don't forget to addthe framework to your project!#if TARGET_OS_IPHONEif (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"6.0")) {_openUDID = [[[UIDevice currentDevice] identifierForA_______] UUIDString];
# error                                                         ^ read comments above, fix accordingly, and remove this #error line}
#endif*/// Next we generate a UUID.// UUIDs (Universally Unique Identifiers), also known as GUIDs (Globally Unique Identifiers) or IIDs// (Interface Identifiers), are 128-bit values guaranteed to be unique. A UUID is made unique over // both space and time by combining a value unique to the computer on which it was generated—usually the// Ethernet hardware address—and a value representing the number of 100-nanosecond intervals since // October 15, 1582 at 00:00:00.// We then hash this UUID with md5 to get 32 bytes, and then add 4 extra random bytes// Collision is possible of course, but unlikely and suitable for most industry needs (e.g. aggregate tracking)//if (_openUDID==nil) {CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);CFStringRef cfstring = CFUUIDCreateString(kCFAllocatorDefault, uuid);const char *cStr = CFStringGetCStringPtr(cfstring,CFStringGetFastestEncoding(cfstring));unsigned char result[16];CC_MD5( cStr, (int)strlen(cStr), result );CFRelease(uuid);CFRelease(cfstring);_openUDID = [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%08lx",result[0], result[1], result[2], result[3], result[4], result[5], result[6], result[7],result[8], result[9], result[10], result[11],result[12], result[13], result[14], result[15],(unsigned long)(arc4random() % NSUIntegerMax)];}// Call to other developers in the Open Source community://// feel free to suggest better or alternative "UDID" generation code above.// NOTE that the goal is NOT to find a better hash method, but rather, find a decentralized (i.e. not web-based)// 160 bits / 20 bytes random string generator with the fewest possible collisions.// return _openUDID;
}// Main public method that returns the OpenUDID
// This method will generate and store the OpenUDID if it doesn't exist, typically the first time it is called
// It will return the null udid (forty zeros) if the user has somehow opted this app out (this is subject to 3rd party implementation)
// Otherwise, it will register the current app and return the OpenUDID
//+ (NSString*) value {static NSString*ygOpenUDID = nil;if(!ygOpenUDID){@try {ygOpenUDID = [OpenUDID valueWithError:nil]?:@"";}@catch (NSException *exception) {return @"";}}return ygOpenUDID?:@"";
}+ (NSString*) valueWithError:(NSError **)error {if (kOpenUDIDSessionCache!=nil) {if (error!=nil)*error = [NSError errorWithDomain:kOpenUDIDDomaincode:kOpenUDIDErrorNoneuserInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"OpenUDID in cache from first call",@"description", nil]];return kOpenUDIDSessionCache;}NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];// The AppUID will uniquely identify this app within the pastebins//NSString * appUID = [defaults objectForKey:kOpenUDIDAppUIDKey];if(appUID == nil){// generate a new uuid and store it in user defaultsCFUUIDRef uuid = CFUUIDCreate(NULL);appUID = (NSString *) CFUUIDCreateString(NULL, uuid);CFRelease(uuid);[appUID autorelease];}NSString* openUDID = nil;NSString* myRedundancySlotPBid = nil;NSDate* optedOutDate = nil;BOOL optedOut = NO;BOOL saveLocalDictToDefaults = NO;BOOL isCompromised = NO;// Do we have a local copy of the OpenUDID dictionary?// This local copy contains a copy of the openUDID, myRedundancySlotPBid (and unused in this block, the local bundleid, and the timestamp)//id localDict = [defaults objectForKey:kOpenUDIDKey];if ([localDict isKindOfClass:[NSDictionary class]]) {localDict = [NSMutableDictionary dictionaryWithDictionary:localDict]; // we might need to set/overwrite the redundancy slotopenUDID = [localDict objectForKey:kOpenUDIDKey];myRedundancySlotPBid = [localDict objectForKey:kOpenUDIDSlotKey];optedOutDate = [localDict objectForKey:kOpenUDIDOOTSKey];optedOut = optedOutDate!=nil;OpenUDIDLog(@"localDict = %@",localDict);}// Here we go through a sequence of slots, each of which being a UIPasteboard created by each participating app// The idea behind this is to both multiple and redundant representations of OpenUDIDs, as well as serve as placeholder for potential opt-out//NSString* availableSlotPBid = nil;NSMutableDictionary* frequencyDict = [NSMutableDictionary dictionaryWithCapacity:kOpenUDIDRedundancySlots];for (int n=0; n<kOpenUDIDRedundancySlots; n++) {NSString* slotPBid = [NSString stringWithFormat:@"%@%d",kOpenUDIDSlotPBPrefix,n];
#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATORUIPasteboard* slotPB = [UIPasteboard pasteboardWithName:slotPBid create:NO];
#elseNSPasteboard* slotPB = [NSPasteboard pasteboardWithName:slotPBid];
#endifOpenUDIDLog(@"SlotPB name = %@",slotPBid);if (slotPB==nil) {// assign availableSlotPBid to be the first one availableif (availableSlotPBid==nil) availableSlotPBid = slotPBid;} else {NSDictionary* dict = [OpenUDID _getDictFromPasteboard:slotPB];NSString* oudid = [dict objectForKey:kOpenUDIDKey];OpenUDIDLog(@"SlotPB dict = %@",dict);if (oudid==nil) {// availableSlotPBid could inside a non null slot where no oudid can be foundif (availableSlotPBid==nil) availableSlotPBid = slotPBid;} else {// increment the frequency of this oudid keyint count = [[frequencyDict valueForKey:oudid] intValue];[frequencyDict setObject:[NSNumber numberWithInt:++count] forKey:oudid];}// if we have a match with the app unique id,// then let's look if the external UIPasteboard representation marks this app as OptedOutNSString* gid = [dict objectForKey:kOpenUDIDAppUIDKey];if (gid!=nil && [gid isEqualToString:appUID]) {myRedundancySlotPBid = slotPBid;// the local dictionary is prime on the opt-out subject, so ignore if already opted-out locallyif (optedOut) {optedOutDate = [dict objectForKey:kOpenUDIDOOTSKey];optedOut = optedOutDate!=nil;   }}}}// sort the Frequency dict with highest occurence count of the same OpenUDID (redundancy, failsafe)// highest is last in the list//NSArray* arrayOfUDIDs = [frequencyDict keysSortedByValueUsingSelector:@selector(compare:)];NSString* mostReliableOpenUDID = (arrayOfUDIDs!=nil && [arrayOfUDIDs count]>0)? [arrayOfUDIDs lastObject] : nil;OpenUDIDLog(@"Freq Dict = %@\nMost reliable %@",frequencyDict,mostReliableOpenUDID);// if openUDID was not retrieved from the local preferences, then let's try to get it from the frequency dictionary above//if (openUDID==nil) {        if (mostReliableOpenUDID==nil) {// this is the case where this app instance is likely to be the first one to use OpenUDID on this device// we create the OpenUDID, legacy or semi-random (i.e. most certainly unique)//openUDID = [OpenUDID _generateFreshOpenUDID];} else {// or we leverage the OpenUDID shared by other apps that have already gone through the process// openUDID = mostReliableOpenUDID;}// then we create a local representation//if (localDict==nil) { localDict = [NSMutableDictionary dictionaryWithCapacity:4];[localDict setObject:openUDID forKey:kOpenUDIDKey];[localDict setObject:appUID forKey:kOpenUDIDAppUIDKey];[localDict setObject:[NSDate date] forKey:kOpenUDIDTSKey];if (optedOut) [localDict setObject:optedOutDate forKey:kOpenUDIDTSKey];saveLocalDictToDefaults = YES;}}else {// Sanity/tampering check//if (mostReliableOpenUDID!=nil && ![mostReliableOpenUDID isEqualToString:openUDID])isCompromised = YES;}// Here we store in the available PB slot, if applicable//OpenUDIDLog(@"Available Slot %@ Existing Slot %@",availableSlotPBid,myRedundancySlotPBid);if (availableSlotPBid!=nil && (myRedundancySlotPBid==nil || [availableSlotPBid isEqualToString:myRedundancySlotPBid])) {
#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATORUIPasteboard* slotPB = [UIPasteboard pasteboardWithName:availableSlotPBid create:YES];[slotPB setPersistent:YES];
#elseNSPasteboard* slotPB = [NSPasteboard pasteboardWithName:availableSlotPBid];
#endif// save slotPBid to the defaults, and remember to save later//if (localDict) {[localDict setObject:availableSlotPBid forKey:kOpenUDIDSlotKey];saveLocalDictToDefaults = YES;}// Save the local dictionary to the corresponding UIPasteboard slot//if (openUDID && localDict)[OpenUDID _setDict:localDict forPasteboard:slotPB];}// Save the dictionary locally if applicable//if (localDict && saveLocalDictToDefaults)[defaults setObject:localDict forKey:kOpenUDIDKey];// If the UIPasteboard external representation marks this app as opted-out, then to respect privacy, we return the ZERO OpenUDID, a sequence of 40 zeros...// This is a *new* case that developers have to deal with. Unlikely, statistically low, but still.// To circumvent this and maintain good tracking (conversion ratios, etc.), developers are invited to calculate how many of their users have opted-out from the full set of users.// This ratio will let them extrapolate convertion ratios more accurately.//if (optedOut) {if (error!=nil) *error = [NSError errorWithDomain:kOpenUDIDDomaincode:kOpenUDIDErrorOptedOutuserInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Application with unique id %@ is opted-out from OpenUDID as of %@",appUID,optedOutDate],@"description", nil]];kOpenUDIDSessionCache = [[NSString stringWithFormat:@"%040x",0] retain];return kOpenUDIDSessionCache;}// return the well earned openUDID!//if (error!=nil) {if (isCompromised)*error = [NSError errorWithDomain:kOpenUDIDDomaincode:kOpenUDIDErrorCompromiseduserInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Found a discrepancy between stored OpenUDID (reliable) and redundant copies; one of the apps on the device is most likely corrupting the OpenUDID protocol",@"description", nil]];else*error = [NSError errorWithDomain:kOpenUDIDDomaincode:kOpenUDIDErrorNoneuserInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"OpenUDID succesfully retrieved",@"description", nil]];}kOpenUDIDSessionCache = [openUDID retain];return kOpenUDIDSessionCache;
}+ (void) setOptOut:(BOOL)optOutValue {// init call[OpenUDID value];NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];// load the dictionary from local cache or create oneid dict = [defaults objectForKey:kOpenUDIDKey];if ([dict isKindOfClass:[NSDictionary class]]) {dict = [NSMutableDictionary dictionaryWithDictionary:dict];} else {dict = [NSMutableDictionary dictionaryWithCapacity:2];}// set the opt-out date or remove key, according to parameterif (optOutValue)[dict setObject:[NSDate date] forKey:kOpenUDIDOOTSKey];else[dict removeObjectForKey:kOpenUDIDOOTSKey];// store the dictionary locally[defaults setObject:dict forKey:kOpenUDIDKey];OpenUDIDLog(@"Local dict after opt-out = %@",dict);// reset memory cache kOpenUDIDSessionCache = nil;}@end

作者:宋奕Ekis
链接:https://www.jianshu.com/p/69a526f72639
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

iOS各种设备识别码IDFA、UDID、UUID、MAC、OpenUDID、IDFV相关推荐

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

    http://www.ithao123.cn/content-8688001.html iOS获取设备唯一标识的各种方法?IDFA.IDFV.UDID分别是什么含义? [摘要:1.UDID (Uniq ...

  2. iOS - 获取设备标识符UUID/UDID/IMEI等

    关于获取设备各种标识符,最近整理了一下.基本上有IDFA.IDFV.IMEI.IMSI.UUID.UDID.MAC地址; 想要获取系统唯一标识符的话,比如说不管用户卸载还是重新安装都可以唯一标识的可以 ...

  3. ios如何看idfv_iOS-理解 : UDID、UUID、IDFA、IDFV

    关于UUID的理解 : 英文名称是:Universally Unique Identifier,翻译过来就是通用唯一标识符. UUID是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一 ...

  4. iOS获取设备UDID/IMEI信息

    标题iOS获取设备UDID/IMEI信息 通过Safari与mobileconfig获取iOS设备UDID UDID (Unique Device Identifier),唯一标示符,是iOS设备的一 ...

  5. iOS系统 查看设备序列号 获取UDID

    iOS系统的设备标识即UDID,长度为40位,如:85f3c1bcb9ca8ad8b2931219072cf05382a6da09 获取UDID,可以使用手机端访问UDID查询网站在线查询,也可以通过 ...

  6. iOS获取设备ID总结

    UDID 简介:UDID的全称是Unique Device Identifier,顾名思义,它就是苹果IOS设备的唯一识别码,它由40个字符的字母和数字组成.在很多需要限制一台设备一个账号的应用中经常 ...

  7. iOS获取设备的唯一标识的方法总结以及最好的方法

    各种获取设备唯一标识的方法介绍 一.UDID(Unique Device Identifier) UDID的全称是Unique Device Identifier,它就是苹果iOS设备的唯一识别码,它 ...

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

    各种获取设备唯一标识的方法介绍 一.UDID(Unique Device Identifier) UDID的全称是Unique Device Identifier,它就是苹果iOS设备的唯一识别码,它 ...

  9. iOS获取设备唯一标识

    前言 目前市面应用普遍采用用户体验,涉及到部分重要功能时候才提醒用户注册账户,而用户之前的操作,比如收藏,点赞,关注等内容需要同时关联进注册的账户,那么根据什么记录用户的操作信息就尤为重要.下面就列出 ...

最新文章

  1. RSocket:又一个REST的挑战者
  2. 【学习笔记】31、Python中的断言
  3. 揭开数字货币交易所“日入千万”的神秘面纱
  4. 【免费活动】字节跳动背后的音视频技术揭秘
  5. django 后台日期_Django中的日期和时间格式 DateTimeField
  6. Java架构-面试怎么谈薪资——让自己的利益最大化
  7. 登陆窗体相关的控件 1124
  8. [Ext JS]8.3 Sencha Studio安装与快速介绍之一
  9. 关于大数据平台,这有一套完整的方法论,你确定不收藏?
  10. 用计算机怎么按四分之三次方等于多少,八十一分之十六的负四分之三次方怎么算,要具体过程,答案是多少?...
  11. HashMap初始容量指定规则
  12. Imagenet的中英对应分类
  13. esxi - 加装vmware titan xp显卡配置
  14. apicloud传递数据
  15. DDR中的ODT功能详解及波形对比
  16. python time strptime_Python time.strptime方法代碼示例
  17. 微信小程序-计算器小程序《从零开始学微信小程序》
  18. python之tkinter模块——基于tk得图片文字
  19. codevs1013 求先序排列 string黑科技[三星]
  20. 创建数据库报错--MySQL server is running with the --super-read-only option

热门文章

  1. python定位二维码_图像中二维码的检测和定位
  2. 一位苦逼程序员的找工作经历
  3. 史上最全Java面试题全集(上)
  4. 计算机的cup颗数、核数、线程数
  5. linux tar源码,linux之tar命令备份
  6. homelede软路由设置方法_低成本组装一台LEDE软路由
  7. 计算两个数据的百分比
  8. kickstarter众筹
  9. Docker Windows桌面版安装 Windows家庭版伪装成专业版系统
  10. hive日期函数,求日期差等,datediff,date_add,date_sub,add_months