戳我
戳我
文章目录
  1. UDID (Unique Device Identifier Description)
  2. MAC (Medium/Media Access Control)
  3. IDFA (Identifier For Identifier)
  4. IDFV(Identifier For Vendor)
  5. UUID (Universally Unique Identifier)
    1. CFUUID
    2. NSUUID
  6. 比较完善的做法

获取设备唯一标识符

获取设备的唯一标识符具体可以分为如下几种:UDID MAC IDFA IDFV UUID. 下面分别来介绍下.

UDID (Unique Device Identifier Description)

UDID 是由子母和数字组成的40个字符串的序号, 用来区别每一个唯一的iOS设备, 这些编码看起来是随机的, 但实际上是跟硬件设备特点相关联的. 我们平时用开发者账号在设备上安装测试应用时, 往开发者账号上添加的就是设备的UDID.

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

由于UDID是跟设备唯一对应的, 许多开发者试图通过UDID获取到用户的真实姓名, 密码, 地址等隐私数据. 为了避免引起麻烦, 苹果在iOS 5.0 的时候废除了UDID的代码获取权限, 现在应用试图获取UDID已被禁止且不允许上架.

MAC (Medium/Media Access Control)

这个就是常说的网卡地址, 一部iPhone上可能有多个MAC地址, 包括WIFI的, SIM的等. 具有全球唯一性, 这样就可以非常好的标识设备唯一性.

- (NSString *) getMacAddress {  
        int mib[6];  
        size_t len;  
        charchar *buf;  
        unsigned charchar *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 charchar *)LLADDR(sdl);  
        NSString *outstring = [NSString stringWithFormat:@"%02x:%02x:%02x:%02x:%02x:%02x", *ptr, *(ptr+1), *(ptr+2), *(ptr+3), *(ptr+4), *(ptr+5)];  
        free(buf);  

        return [outstring uppercaseString]; 
    }

但MAC地址跟UDID一样, 存在隐私问题, 所以在 iOS 7.0 之后, Mac地址再次遭到苹果的无情封杀. 如果使用之前的方法请求Mac地址都会返回一个固定值 02:00:00:00:00:00, 这一条路再次被堵死.

IDFA (Identifier For Identifier)

这是 iOS 6.0中提供的一个新方法, 在同一个设备上的所有App都会取到相同的值, 是苹果专门给各广告提供商用来追踪用户而设的.

#import <AdSupport/AdSupport.h>

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

IDFA并不是唯一不变的, 如果用户完全重置系统(设置程序 -> 通用 -> 还原 -> 还原位置与隐私), 这个广告标示符会重新生成. 另外如果用户明确的还原广告(设置程序-> 通用 -> 关于本机 -> 广告 -> 还原广告标示符), 那么广告标示符也会重新生成. 在iOS 10.0以后如果用户打开限制广告跟踪(设置程序-> 通用 -> 关于本机 -> 广告 -> 限制广告跟踪), 则获取到的IDFA为一个固定值00000000-0000-0000-0000-000000000000. 因此, 通过IDFA也无法唯一标识一个设备.
如果使用IDFA, 在上传Appstore时必须在iTunes Connect中的上传页面进行相应的设置.

IDFV(Identifier For Vendor)

IDFV是给Vendor标识用户用的, 每个设备在所属同一个Vendor的应用里, 都有相同的值. 其中的Vendor是指应用提供商, 准确的说, 是通过BundleID的反转的前两部分进行匹配, 如果相同就是同一个Vendor, 例如对于com.abc.app1, com.abc.app2 这两个BundleID来说, 就属于同一个Vendor, 共享同一个IDFV的值. 当然, 对于同一个设备不同Vendor的话, 获取到的值是不同的. 和IDFA不同的是, IDFV的值是一定能取到的. 它是iOS 6中新增的.

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

但是使用IDFV也会存在一些问题, 如果用户将属于此Vendor的所有App卸载, 则IDFV的值会被重置, 即再重装此Vendor的App, IDFV的值也会和之前的不同.

UUID (Universally Unique Identifier)

UUID是Universally Unique Identifier的缩写, 中文意思是通用唯一识别码. 它是苹果提供的一个获取大随机数的方法, 据说UUID随机数算法得到的数重复概率为170亿分之一. 这样, 每个人都可以建立不与其它人冲突的 UUID.

CFUUID

从iOS 2.0开始, CFUUID就已经出现了. 它是CoreFoundation包的一部分, 因此API属于C语言风格.

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

NSUUID

NSUUID在iOS 6.0中才出现, 这跟CFUUID几乎完全一样, 只不过它是Objective-C接口. 通过下面的代码可以获得一个UUID字符串:

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

比较完善的做法

//获取UQID
+ (NSString *)getUQID {
    //从本地沙盒取
    NSString *uqid = [[NSUserDefaults standardUserDefaults] objectForKey:UQID_KEY];

    if (!uqid) {
        //从keychain取
        uqid = (NSString *)[YDKeyChain readObjectForKey:UQID_KEY];

        if (uqid) {
            [[NSUserDefaults standardUserDefaults] setObject:uqid forKey:UQID_KEY];
            [[NSUserDefaults standardUserDefaults] synchronize];

        } else {
            //从pasteboard取
            UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
            id data = [pasteboard dataForPasteboardType:UQID_KEY];
            if (data) {
                uqid = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
            }

            if (uqid) {
                [[NSUserDefaults standardUserDefaults] setObject:uqid forKey:UQID_KEY];
                [[NSUserDefaults standardUserDefaults] synchronize];
                [YDKeyChain saveObject:uqid forKey:UQID_KEY];

            } else {

                //获取idfa
                uqid = [self getIDFA];

                //idfa获取失败的情况,获取idfv
                if (!uqid || [uqid isEqualToString:@"00000000-0000-0000-0000-000000000000"]) {
                    uqid = [self getIDFV];

                    //idfv获取失败的情况,获取uuid
                    if (!uqid) {
                        uqid = [self getUUID];
                    }
                }

                [[NSUserDefaults standardUserDefaults] setObject:uqid forKey:UQID_KEY];
                [[NSUserDefaults standardUserDefaults] synchronize];

                [YDKeyChain saveObject:uqid forKey:UQID_KEY];

                UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
                NSData *data = [uqid dataUsingEncoding:NSUTF8StringEncoding];
                [pasteboard setData:data forPasteboardType:UQID_KEY];

            }
        }
    }
    return uqid;