欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2024-02-16 10:09:40
8种ios获取设备唯一标识的方法,希望对大家有用。 udid udid(unique device identifier),ios 设备的唯一识别码,是一个40位十六进...

8种ios获取设备唯一标识的方法,希望对大家有用。

udid

udid(unique device identifier),ios 设备的唯一识别码,是一个40位十六进制序列(越狱的设备通过某些工具可以改变设备的 udid),移动网络可以利用 udid 来识别移动设备。

许多开发者把 udid 跟用户的真实姓名、密码、住址、其它数据关联起来,网络窥探者会从多个应用收集这些数据,然后顺藤摸瓜得到这个人的许多隐私数据,同时大部分应用确实在频繁传输 udid 和私人信息。 为了避免集体诉讼,苹果最终决定在 ios 5 的时候,将这一惯例废除。

现在应用试图获取 udid 已被禁止且不允许上架。

mac 地址

mac(medium / media access control)地址,用来表示互联网上每一个站点的标示符,是一个六个字节(48位)的十六进制序列。前三个字节是由 ieee 的注册管理机构 ra 负责给不同厂家分配的”编制上唯一的标示符(organizationally unique identifier)”,后三个字节由各厂家自行指派给生产的适配器接口,称为扩展标示符。

mac 地址在网络上用来区分设备的唯一性,接入网络的设备都有一个mac地址,他们肯定都是唯一的。一部 iphone 上可能有多个 mac 地址,包括 wifi 的、sim 的等,但是 itouch 和 ipad 上就有一个 wifi 的,因此只需获取 wifi 的 mac 地址就好了。一般会采取 md5(mac 地址 + bundleid)获取唯一标识。

但是 mac 地址和 udid 一样,存在隐私问题, ios 7 之后,所有设备请求 mac 地址会返回一个固定值,这个方法也不攻自破了。

openudid

udid 被弃用后,广大开发者需要寻找一个可以替代的 udid,并且不受苹果控制的方案,由此,openudid 成为了当时使用最广泛的开源 udid 代替方案。openudid 利用一个非常巧妙的方法在不同程序间存储标示符:在粘贴板中用了一个特殊的名称来存储标示符,通过这种方法,其他应用程序也可以获取。

苹果在 ios 7 之后对粘贴板做了限制,导致同一个设备上的应用间,无法再共享一个 openudid。

uuid + 自己存储

uuid(universally unique identifier),通用唯一标示符,是一个32位的十六进制序列,使用小横线来连接:8-4-4-4-12,通过 nsuuid(ios 6 之后)[nsuuid uuid].uuidstring 或者 cfuuid(ios 2 之后) cfbridgingrelease(cfuuidcreatestring(kcfallocatordefault, cfuuidcreate(kcfallocatordefault))) 来获取,但是每次获取的值都不一样,需要自己存储。

推送 token + bundleid

推送 token 保证设备唯一,但是必须有网络情况下才能工作,该方法不依赖于设备本身,但依赖于 apple push,而 apple push 有时候会抽风的。

idfa

idfa-identifierforidentifier(广告标示符),在同一个设备上的所有 app 都会取到相同的值,是苹果专门给各广告提供商用来追踪用户而设定的。虽然 iphone 默认是允许追踪的,而且一般用户都不知道有这么个设置,但是用户可以在 设置 - 隐私 - 广告追踪 里重置此 id 的值,或者限制此 id 的使用,所以有可能会取不到值。

idfv

idfv-identifierforvendor(vendor 标示符),通过 [uidevice currentdevice].identifierforvendor.uuidstring 来获取。是通过 bundleid 的反转的前两部分进行匹配,如果相同是同一个 vendor ,例如对于 com.mayan.app_1 和 com.mayan.app_2 这两个 bundleid 来说,就属于同一个 vendor ,共享同一个 idfv,和 idfa 不同的是,idfv 的值一定能取到的,所以非常适合于作为内部用户行为分析的主 id 来识别用户。但是用户删除了该 app ,则 idfv 值会被重置,再次安装此 app ,idfv 的值和之前的不同。

idfv + keychain

通过以上几种储存唯一标识的方法的分析,总结一下各有优劣。很多方法被苹果禁止或者漏洞太多,越来越不被开发者使用,现在苹果主推 idfa 和 idfv 这两种方法,分别对外和对内,但是 idfv 在 app 重新安装时会更改,所以我的方法是通过第一次生成的 idfv 存储到 keychain 中,以后每次获取标识符都从 keychain 中获取。

#import <uikit/uikit.h>
@interface myvendortoll : nsobject
+ (nsstring *)getidfv;
@end

#import "myvendortoll.h"
#import "mykeychaintool.h"

@implementation myvendortoll


+ (nsstring *)getidfv
{
 nsstring *idfv = (nsstring *)[mykeychaintool load:@"idfv"];

 if ([idfv isequaltostring:@""] || !idfv) {

  idfv = [uidevice currentdevice].identifierforvendor.uuidstring;
  [mykeychaintool save:@"idfv" data:idfv];
 }

 return idfv;
}

@end

#import <foundation/foundation.h>

@interface mykeychaintool : nsobject


+ (void)save:(nsstring *)service data:(id)data;
+ (id)load:(nsstring *)service;
+ (void)deletekeydata:(nsstring *)service;

@end

#import "mykeychaintool.h"

@implementation mykeychaintool


+ (nsmutabledictionary *)getkeychainquery:(nsstring *)service {
 return [nsmutabledictionary dictionarywithobjectsandkeys:
   (id)ksecclassgenericpassword,(id)ksecclass,
   service, (id)ksecattrservice,
   service, (id)ksecattraccount,
   (id)ksecattraccessibleafterfirstunlock,(id)ksecattraccessible,
   nil];
}

+ (void)save:(nsstring *)service data:(id)data {
 //get search dictionary
 nsmutabledictionary *keychainquery = [self getkeychainquery:service];
 //delete old item before add new item
 secitemdelete((cfdictionaryref)keychainquery);
 //add new object to search dictionary(attention:the data format)
 [keychainquery setobject:[nskeyedarchiver archiveddatawithrootobject:data] forkey:(id)ksecvaluedata];
 //add item to keychain with the search dictionary
 secitemadd((cfdictionaryref)keychainquery, null);
}

+ (id)load:(nsstring *)service {
 id ret = nil;
 nsmutabledictionary *keychainquery = [self getkeychainquery:service];
 //configure the search setting
 //since in our simple case we are expecting only a single attribute to be returned (the password) we can set the attribute ksecreturndata to kcfbooleantrue
 [keychainquery setobject:(id)kcfbooleantrue forkey:(id)ksecreturndata];
 [keychainquery setobject:(id)ksecmatchlimitone forkey:(id)ksecmatchlimit];
 cfdataref keydata = null;
 if (secitemcopymatching((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)deletekeydata:(nsstring *)service {
 nsmutabledictionary *keychainquery = [self getkeychainquery:service];
 secitemdelete((cfdictionaryref)keychainquery);
}

@end

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。