• iOS唯一设备ID


    设备ID,简单来说就是一串符号(或者数字),用来唯一标识一台硬件设备。

    对于APP自身产品而言,使用设备唯一ID可以追踪到用户从下载、激活、注册、使用、流失、回归的全流程数据,对产品运营工作非常有帮助。

    对于精准广告和个性化推荐而言,可以使用设备ID进行数据收集、然后进行千人千面的精准营销。

    IMEI

    IMEI是国际移动设备识别码,一串15位的号码,每部通过正规渠道销售的GSM手机均有唯一的IMEI码。

    IMEI码由GSMA协会统一规划,并授权各地区组织进行分配,一般由运营商存储在SIM卡中。

    因此,与Android手机一样,iOS也有IMEI码,可在(设置  -> 通用 -> 关于手机 )中查看。 但在iOS5中已经完全的禁用了它。

    UDID

    UDID(Unique Device Identifier Description)是苹果IOS设备的唯一识别码,由40个字符的字母和数字组成。

    在很多需要限制一台设备一个账号的应用中经常会用到。在iOS5中可以获取到设备的UDID,但在iOS7中已经完全的禁用了它。

    iOS7之前编译的app如果在iOS7上运行,它不会返回设备的UDID,而是会返回一串字符串,以FFFFFFFF开头,跟着identifierForVendor的十六进制值。

    NSString *UDID = [[UIDevice currentDevice] uniqueIdentifier];

    查看设备UDID

    方法1:

    (1)将手机通过数据线查到电脑,并在手机上信任此电脑后,就可以在iTune中查看手机的UDID

    (2)如下可以看到当前手机的UDID(注:如显示为序列号、ECID、型号识别符等,请点击切换到UDID)

    注:新的Mac系统(如:macOS Catalina  版本:10.15.5)已经去掉了iTune了,可从这里查看手机的UDID

    方法2:将手机通过数据线查到电脑,并在手机上信任此电脑后,在Xcode菜单“Window” -- “Devices and Simulators”的Devices面板中查看手机的UDID

    当然,目前想要获取UDID也并不是全无办法,不过过程非常复杂   有兴趣的朋友可以参考这篇博客:通过Safari获取UDID

    OpenUDID

    由于UDID在iOS7中完全的禁用,OpenUDID成为了当时使用最广泛的开源UDID替代方案 

    OpenUDID利用了一个非常巧妙的方法在不同程序间存储标示符 — 在粘贴板中用了一个特殊的名称来存储标示符。通过这种方法,别的程序(同样使用了OpenUDID)知道去什么地方获取已经生成的标示符(而不用再生成一个新的)。

    而且根据贡献者的代码和方法,和一些开发者的经验,如果把使用了OpenUDID方案的应用全部都删除,再重新获取OpenUDID,此时的OpenUDID就跟以前的不一样。可见,这种方法还是不保险。

    值得一提是:OpenUDID库早已经弃用了, 在其官方的博客中也指明了, 停止维护OpenUDID的原因是为了更好的向苹果的举措靠拢。

    UUID

    UUID(Universally Unique IDentifier):通用唯一识别码。它是苹果提供的一个获取大随机数的方法。形如:68753A44-4D6F-1226-9C60-0050E4C00067

    据说UUID随机数算法得到的数重复概率为170亿分之一。这样,每个人都可以建立不与其它人冲突的 UUID。

    CFUUID

    从iOS2.0开始,CFUUID就已经出现了。它是CoreFoundatio包的一部分,因此API属于C语言风格。CFUUIDCreate 方法用来创建CFUUIDRef,并且可以获得一个相应的NSString。

    每次调用CFUUIDCreate,系统都会返回一个新的CFUUID,但是它会保持唯一性。

    CFUUIDRef cfuuid = CFUUIDCreate(kCFAllocatorDefault);
    NSString *cfuuidString = (NSString*)CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, cfuuid));

    NSUUID

    NSUUID在iOS 6中才出现,与CFUUID完全一样,只不过它是Objective-C接口。同样,NSUUID 每次获取的值都会发生变化,但是它会保持唯一性。

    NSString *uuid = [[NSUUID UUID] UUIDString];

    IDFV

    IDFV(Identifier For Vendor)是iOS 6.0系统新增用于替换UDID的接口。是给Vendor标识用户用的,每个设备在所属同一个Vender的应用里,都有相同的值。形如:95955F33-BFBD-48BA-A630-866D2DAE482D

    其中的Vender是指应用提供商。iOS6.x是通过BundleID前两部分来匹配,iOS7.x是通过除了最后一个部分来匹配。如果相同就是同一个Vender,共享同一个idfv的值。

    具体详见:identifierForVendor

    Bundle ID

    iOS 6.x

    iOS 7.x

    com.example.app1

    com.example.app1

    com.example.app1

    com.example.app2

    com.example.app2

    com.example.app2

    com.example.app.app1

    com.example.app.app1

    com.example.app.app1

    com.example.app.app2

    com.example.app.app2

    com.example.app.app2

    example

    example

    example

    和idfa不同的是,idfv的值是一定能取到的,所以非常适合于作为内部用户行为分析的主id,来标识用户,替代OpenUDID。

    如果用户将属于此Vender的所有App卸载,则idfv的值会被重置,即再重装此Vender的App,idfv的值和之前不同。

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

    IDFA

    IDFA是iOS 6.0系统新增广告标示符,适用于对外:例如广告推广,换量等跨应用的用户追踪等。

    在同一个设备上的所有App都会取到相同的值,是苹果专门给各广告提供商用来追踪用户而设的。形如:9C287922-EE26-4501-94B5-DDE6F83E1475

    但如果用户完全重置系统(设置  -> 通用 -> 还原 -> 还原位置与隐私) ,这个广告标示符会重新生成。

    在iOS 10.0之前,用户开启了“限制广告跟踪”(设置 -> 隐私- > 广告 -> 限制广告跟踪)之后,商家一样可以获取idfa,只不过与之前的不一样了。

    在iOS 10.0及以后,开启了该功能, 获取到的idfa为一个固定值00000000-0000-0000-0000-000000000000;同样每次开启再关闭,相应的idfa也会重新生成。

    具体详见:advertisingIdentifier

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

    SimulateIDFA

    由于IDFA在iOS 10.0及以后版本中,可能无法获取,因此有人开发了IDFA的替代方案SimulateIDFA。

    SimulateIDFA是一个github开源库,它会根据一堆设备信息(每个app获取的值都是一样的)生成的一个MD5值。用于标志不同设备。与IDFA格式一样,形如:626363D0-90D4-06BF-C281-384E4E69D3E2

    前16位626363D0-90D4-06BF是由比较稳定的参数组合获得(如下),这前16位只有在系统升级的情况下才会变。

    系统版本(9.3.2// 系统升级会变
    硬件信息(N53AP,iPhone6,2,中国移动46002,1048576000)
    coreServices文件创建更新时间(2015-08-07 23:53:00 +0000,2016-06-07 23:53:09 +0000)  // 系统升级会变
    系统容量(12266725376)  // 系统升级会变

    后16位C281-384E4E69D3E2 由一些比较容易被改变的参数组合生成,比较常见的值变化情况是系统重新启动。

    系统开机时间(1473301191去掉后面的4位数 147330)  // 系统重启会变
    国家代码(CN)
    本地语言(zh-Hans-CN)
    设备名称(XXXX)

    在需要获取 SimulateIDFA的地方调用代码

    NSString *simulateIDFA = [SimulateIDFA createSimulateIDFA];

    OpenIDFA

    由于IDFA在iOS 10.0及以后版本中,可能无法获取,同样,有人开发了IDFA的替代方案OpenIDFA。

    OpenIDFA是一个github开源库,与SimulateIDFA一样,同样是IDFA的一种替代方案。

    OpenIDFA是对下面的参数组合进行MD5

    系统开机时间(1473241127 减去后四位值为 147324)
    系统容量(29230571520)
    系统版本(9.3.4)
    机型(N78AP,iPod5,1)
    国家代码(CN)
    本地语言(zh-Hans-CN)
    一些预装的App  // 由于用的是canOpenURL这个接口,iOS9就已经废了
    时区(Asia/Shanghai)
    当天时间(160804, 16年8月4日)  // 这个值是其每天值都会变化的原因

    与SimulateIDFA相比,OpenIDFA 有一些限制,生成的IDFA会每天变化,在一些极端条件下重复率比较高。

    MAC地址

    MAC地址在网络上用来区分设备的唯一性,接入网络的设备都有一个MAC地址,他们肯定都是不同的,是唯一的。

    一部iPhone上可能有多个MAC地址,包括WIFI的、SIM的等,但是iTouch和iPad上就有一个WIFI的,因此只需获取WIFI的MAC地址就好了,也就是en0的地址。

    MAC地址就如同我们身份证上的身份证号码,具有全球唯一性。

    - (NSString *)macAddress  
    {  
    
        int                 mib[6];  
        size_t              len;  
        char                *buf;  
        unsigned char       *ptr;  
        struct if_msghdr    *ifm;  
        struct sockaddr_dl  *sdl;  
    
        mib[0] = CTL_NET;  
        mib[1] = AF_ROUTE;  
        mib[2] = 0;  
        mib[3] = AF_LINK;  
        mib[4] = NET_RT_IFLIST;  
    
        if ((mib[5] = if_nametoindex("en0")) == 0) {  
            printf("Error: if_nametoindex error/n");  
            return NULL;  
        }  
    
        if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {  
            printf("Error: sysctl, take 1/n");  
            return NULL;  
        }  
    
        if ((buf = malloc(len)) == NULL) {  
            printf("Could not allocate memory. error!/n");  
            return NULL;  
        }  
    
        if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {  
            printf("Error: sysctl, take 2");  
            return NULL;  
        }  
    
        ifm = (struct if_msghdr *)buf;  
        sdl = (struct sockaddr_dl *)(ifm + 1);  
        ptr = (unsigned char *)LLADDR(sdl);  
        NSString *outstring = [NSString stringWithFormat:@"%02x:%02x:%02x:%02x:%02x:%02x", *ptr, *(ptr+1), *(ptr+2), *(ptr+3), *(ptr+4), *(ptr+5)];  
    
        NSLog(@"outString:%@", outstring);  
    
        free(buf);  
    
        return [outstring uppercaseString];  
    }

    但在iOS 7.0之后,如果请求Mac地址都会返回一个固定值:02:00:00:00:00:00

    herody网友实现的UQID

    详见其在github上开源项目:UQIDDemo

    使用KeyChain来提升持久性

    上面的设备ID方案,可保证唯一性,但在持久性上存在或多或少的问题。为了最大限度地维持设备ID的持久性,可以借助KeyChain来保存第一次生成出来的设备ID,然后下次直接从KeyChain读取该ID

    iOS系统中有一个KeyChain(类似于windows的注册表),每个程序都可以往KeyChain中记录数据,而且只能读取到自己程序记录在KeyChain中的数据。

    详细特点如下:

    1. KeyChain与在App的sandbox不一样,即使删除了App,资料依然保存在KeyChain中,如果重新安装了App,还可以从KeyChain中获取数据

    2. KeyChain的数据可以用group的方式,让程序可以在App间共享。共享的条件如下:

    ① 相同的bundle id组:比如这里有两个应用程序: A应用程序使用的provision对应的bundle id是com.jaybin.keychain1,B应用程序使用的provision对应的 bundle id是com.jaybin.keychain2 。那么这两个应用程序就可以共享keychain数据。

    ② 应用程序需要打开Keychain Sharing

    3. keychain的数据以加密的方式存储在设备中

    因此就算我们程序删除掉,系统经过升级以后再安装回来,依旧可以获取到与之前一致的设备ID(系统还原、刷机除外)

    + (NSString *)UUID {
        KeychainItemWrapper *keyChainWrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@"MYAppID" accessGroup:@"com.test.app"];
        NSString *UUID = [keyChainWrapper objectForKey:(__bridge id)kSecValueData];
        
        if (UUID == nil || UUID.length == 0) {
            UUID = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
            [keyChainWrapper setObject:UUID forKey:(__bridge id)kSecValueData];
        }
        
        return UUID;
    }

    注:KeychainItemWrapper工具类详见:github

    参考

    iOS获取设备ID总结

    iOS 获取设备唯一识别码 IDFV+keychain

    iOS获取设备UUID和IDFA

    iOS Device ID 的前世今生

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

    【iOS】获取设备唯一标识符

  • 相关阅读:
    快速排序
    09-HQL:Group、Order
    08-表属性操作
    07-数据导出及动态分区
    06-数据加载
    05-Hive数据操作
    04-表操作2
    03-表操作1
    [原创]IPV4分组的格式
    mysql中having和where的区别
  • 原文地址:https://www.cnblogs.com/kekec/p/12560850.html
Copyright © 2020-2023  润新知