• 获取iOS设备唯一标识

    2020-07-10 23:31:55
    获取用户设备唯一标识
  • 由于我们后台判断App登录时根据唯一标识来判断的在不同终端登录的,如果唯一标识不一样,说明是换了设备,但是我们的用户名密码是我们同一系列的app公用的,所以得保证同一公司旗下的app在同一设备下的唯一标识是同...

    由于我们后台判断App登录时根据唯一标识来判断的在不同终端登录的,如果唯一标识不一样,说明是换了设备,但是我们的用户名密码是我们同一系列的app公用的,所以得保证同一公司旗下的app在同一设备下的唯一标识是同一个,避免出现同一设备登陆同一系列其他app时提示换了设备,说个简单的例子

    如:我的用户名是aaa,淘宝和支付宝可以共用,在我的手机上我可以用aaa同时登陆淘宝和支付宝,但是如果我在另一个手机上使用aaa,那么就需要提示用户换了设备

    1.UDID(Unique Device Identifier)

    //UUID , 已废除

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

    UDID的全称是Unique Device Identifier,它就是苹果iOS设备的唯一识别码,它由40位16进制数的字母和数字组成(越狱的设备通过某些工具可以改变设备的UDID)。移动网络可利用UDID来识别移动设备,但是,从IOS5.0(2011年8月份)开始,苹果宣布将不再支持用uniqueIdentifier方法获取设备的UDID,iOS5以下是可以用的。苹果从iOS5开始就移除了通过代码访问UDID的权限。从2013年5月1日起,试图访问UIDIDs的程序将不再被审核通过,替代的方案是开发者应该使用“在iOS 6中介绍的Vendor或Advertising标示符”。所以UDID是绝对是不能再使用了。

    为什么苹果反对开发人员使用UDID?  
    iOS 2.0版本以后UIDevice提供一个获取设备唯一标识符的方法uniqueIdentifier,通过该方法我们可以获取设备的序列号,这个也是目前为止唯一可以确认唯一的标示符。 许多开发者把UDID跟用户的真实姓名、密码、住址、其它数据关联起来;网络窥探者会从多个应用收集这些数据,然后顺藤摸瓜得到这个人的许多隐私数据。同时大部分应用确实在频繁传输UDID和私人信息。 为了避免集体诉讼,苹果最终决定在iOS 5 的时候,将这一惯例废除,开发者被引导生成一个唯一的标识符,只能检测应用程序,其他的信息不提供。现在应用试图获取UDID已被禁止且不允许上架。

    2.UUID(Universally Unique Identifier)

    • 是基于iOS设备上面某个单个的应用程序,只要用户没有完全删除应用程序,则这个UUID在用户使用该应用程序的时候一直保持不变。如果用户删除了这个应用程序,然后再重新安装,那么这个UUID已经发生了改变
    • 同一设备上的不同应用的UUID是互斥的,即能在改设备上标识应用。所以一些人推测,这个UUID应该是根据设备标识和应用标识生成唯一标识,再经过加密而来的(纯推测)。
    • 官方推荐的方法是,每个应用内创建一个UUID来作为唯一标志,并将之存储,但是这个解决方法明显不能接受!

    缺点:

    • 你每次创建的UUID都是不一样的,意味着,你卸载后重新安装这个软件,生成的UUID就不一样了,无法达到我们将之作为数据分析的唯一标识符的要求。

    关于获取UUID的代码:

    [[UIDevice currentDevice] identifierForVendor];
    

    不过,设备唯一标示的问题仍然没有解决:如果你删除应用然后再次安装,这个identifierForVendor的值就变了。

    3.open UDID

    在iOS 5发布时,uniqueIdentifier被弃用了,这引起了广大开发者需要寻找一个可以替代UDID,并且不受苹果控制的方案。由此OpenUDID成为了当时使用最广泛的开源UDID替代方案。OpenUDID在工程中实现起来非常简单,并且还支持一系列的广告提供商。

    OpenUDID利用了一个非常巧妙的方法在不同程序间存储标示符 — 在粘贴板中用了一个特殊的名称来存储标示符。通过这种方法,别的程序(同样使用了OpenUDID)知道去什么地方获取已经生成的标示符(而不用再生成一个新的)。而且根据贡献者的代码和方法,和一些开发者的经验,如果把使用了OpenUDID方案的应用全部都删除,再重新获取OpenUDID,此时的OpenUDID就跟以前的不一样。可见,这种方法还是不保险。 
    但是OpenUDID库早已经弃用了, 在其官方的博客中也指明了, 停止维护OpenUDID的原因是为了更好的向苹果的举措靠拢, 还指明了MAC Address不是一个好的选择。

    4.MAC Address

    4.1.这个MAC地址是指什么?有什么用?

    MAC(Medium/Media Access Control)地址,用来表示互联网上每一个站点的标识符,采用十六进制数表示,共六个字节(48位)。其中,前三个字节是由IEEE的注册管理机构 RA负责给不同厂家分配的代码(高位24位),也称为“编制上唯一的标识符” (Organizationally Unique Identifier),后三个字节(低位24位)由各厂家自行指派给生产的适配器接口,称为扩展标识符(唯一性)。 
    MAC地址在网络上用来区分设备的唯一性,接入网络的设备都有一个MAC地址,他们肯定都是不同的,是唯一的。一部iPhone上可能有多个MAC地址,包括WIFI的、SIM的等,但是iTouch和iPad上就有一个WIFI的,因此只需获取WIFI的MAC地址就好了,也就是en0的地址。 
    形象的说,MAC地址就如同我们身份证上的身份证号码,具有全球唯一性。这样就可以非常好的标识设备唯一性,类似与苹果设备的UDID号,通常的用途有: 
    1)用于一些统计与分析目的,利用用户的操作习惯和数据更好的规划产品; 
    2)作为用户ID来唯一识别用户,可以用游客身份使用app又能在服务器端保存相应的信息,省去用户名、密码等注册过程。

    4.2.如何使用Mac地址生成设备的唯一标识呢?

    主要分三种: 
    1、直接使用“MAC Address” 
    2、使用“MD5(MAC Address)” 
    3、使用“MD5(Mac Address+bundle_id)”获得“机器+应用”的唯一标识(bundle_id 是应用的唯一标识)

    iOS7之前,因为Mac地址是唯一的, 一般app开发者会采取第3种方式来识别安装对应app的设备。为什么会使用它?在iOS5之前,都是使用UDID的,后来被禁用。苹果推荐使用UUID 但是也有诸多问题,从而使用MAC地址。而MAC地址跟UDID一样,存在隐私问题,现在苹果新发布的iOS7上,如果请求Mac地址都会返回一个固定值,那么Mac Address+bundle_id这个值大家的设备都变成一致的啦,跟UDID一样相当于被禁用, 所以Mac Address 是不能够被使用为获取设备唯一标识的。

    5.广告标示符(IDFA-identifierForIdentifier)

    广告标识符,在同一个设备上的所有App都会取到相同的值,是苹果专门给各广告提供商用来追踪用户而设的。但好在Apple默认是允许追踪的,而且一般用户都不知道有这么个设置,所以基本上用来监测推广效果,是戳戳有余了。 
    它是iOS 6中另外一个新的方法,提供了一个方法advertisingIdentifier,通过调用该方法会返回一个NSUUID实例,最后可以获得一个UUID,由系统存储着的。

    #import <AdSupport/AdSupport.h>
        NSString *adId = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
    • 1
    • 2

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

    6.Vendor标示符 (IDFV-identifierForVendor)

    Vendor标示符,是给Vendor标识用户用的,每个设备在所属同一个Vender的应用里,都有相同的值。其中的Vender是指应用提供商,但准确点说,是通过BundleID的反转的前两部分进行匹配,如果相同就是同一个Vender,例如对于com.taobao.app1, com.taobao.app2 这两个BundleID来说,就属于同一个Vender,共享同一个IDFV的值。和IDFA不同的是,IDFV的值是一定能取到的,所以非常适合于作为内部用户行为分析的主id,来标识用户,替代OpenUDID。 
    它是iOS 6中新增的,跟advertisingIdentifier一样,该方法返回的是一个 NSUUID对象,可以获得一个UUID。如果满足条件“相同的一个程序里面-相同的vendor-相同的设备”,那么获取到的这个属性值就不会变。如果是“相同的程序-相同的设备-不同的vendor,或者是相同的程序-不同的设备-无论是否相同的vendor”这样的情况,那么这个值是不会相同的。

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

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

    7.推送token+bundle_id

    推送token+bundle_id的方法: 
    1、应用中增加推送用来获取token 
    2、获取应用bundle_id 
    3、根据token+bundle_id进行散列运算

    apple push token保证设备唯一,但必须有网络情况下才能工作,该方法并不是依赖于设备本身,而是依赖于apple push机制,所以当苹果push做出改变时, 你获取所谓的唯一标识也就随之失效了。所以此方法还是不可取的。


    8. 获取设备唯一标识符的推荐新方案

    思路:

    • 通过调用CFFUUIDCreate函数来生成机器唯一标识符UUID。但每次调用该函数返回的字符串都不一样,所以第一次调用后需把该字符串存储起来。
    • 尽管CFFUUIDCreate每次获取的UUID会发生变化,最理想的是可以保存在钥匙串keychain里面,并以此作为标识用户设备唯一标识符

    2.1 关于获取UUID的官方方案

    关于获取UUID,这是官方API的建议方法:

    - (NSString *) uniqueString
    {
       CFUUIDRef unique = CFUUIDCreate(kCFAllocatorDefault);
       NSString *result = [(NSString *)CFUUIDCreateString(kCFAllocatorDefault, unique) autorelease];
       CFRelease(unique);
       return result;
    }
    

    2.2 基于SSKeychain的唯一识别码方案

    如上获取的UUID,基于Git上的一个第三方库SSKeychain,可以将UUID保存在keychain里面,每次调用先检查钥匙串里面有没有,有则使用,没有则写进去,保证其唯一性,

    参考代码:

    
    - (NSString *)getDeviceUUID
    {
        
        NSString *uuidStr = [SSKeychain passwordForService:@"com.test.app1" account:@"user"];
        if (!uuidStr || [uuidStr isEqualToString:@""])
        {
            CFUUIDRef uuidRef = CFUUIDCreate(kCFAllocatorDefault);
            uuidStr = (__bridge NSString *)CFUUIDCreateString(kCFAllocatorDefault ,uuidRef);
            [SSKeychain setPassword:[NSString stringWithFormat:@"%@", uuidStr] forService:@"com.test.app1"account:@"user"];
        }
        return uuidStr;
    

    }

    如果需要同一系列产品共用唯一标识,在图下的地方加入bundleID进行绑定

    附上demo地址点击打开链接



    展开全文
  • iOS系统中,获取设备唯一标识的方法有很多:一.UDID(Unique Device Identifier) UDID的全称是Unique Device Identifier,它就是苹果IOS设备的唯一识别码,它由40个字符的字母和数字组成(越狱的设备通过某些工具...

    在iOS系统中,获取设备唯一标识的方法有很多:
    一.UDID(Unique Device Identifier)

    UDID的全称是Unique Device Identifier,它就是苹果IOS设备的唯一识别码,它由40个字符的字母和数字组成(越狱的设备通过某些工具可以改变设备的UDID)。移动网络可利用UDID来识别移动设备,但是,从IOS5.0(2011年8月份)开始,苹果宣布将不再支持用uniqueIdentifier方法获取设备的UDID,iOS5以下是可以用的。在2013年3月21日苹果已经通知开发者:从2013年5月1日起,访问UDID的程序将不再被审核通过,替代的方案是开发者应该使用“在iOS 6中介绍的Vendor或Advertising标示符”。所以UDID是绝对不能用啦。


    二.UUID(Universally Unique Identifier)

    UUID是Universally Unique Identifier的缩写,中文意思是通用唯一识别码。它是让分布式系统中的所有元素,都能有唯一的辨识资讯,而不需要透过中央控制端来做辨识资讯的指定。这样,每个人都可以建立不与其它人冲突的 UUID。在此情况下,就不需考虑数据库建立时的名称重复问题。苹果公司建议使用UUID为应用生成唯一标识字符串。


    三.MAC Address

    这个MAC地址是指什么?有什么用?
           MAC(Medium/Media Access Control)地址,用来表示互联网上每一个站点的标识符,采用十六进制数表示,共六个字节(48位)。其中,前三个字节是由IEEE的注册管理机构 RA负责给不同厂家分配的代码(高位24位),也称为“编制上唯一的标识符” (Organizationally Unique Identifier),后三个字节(低位24位)由各厂家自行指派给生产的适配器接口,称为扩展标识符(唯一性)。
        MAC地址在网络上用来区分设备的唯一性,接入网络的设备都有一个MAC地址,他们肯定都是不同的,是唯一的。一部iPhone上可能有多个MAC地址,包括WIFI的、SIM的等,但是iTouch和iPad上就有一个WIFI的,因此只需获取WIFI的MAC地址就好了,也就是en0的地址。
          形象的说,MAC地址就如同我们身份证上的身份证号码,具有全球唯一性。这样就可以非常好的标识设备唯一性,类似与苹果设备的UDID号,通常的用途有:1)用于一些统计与分析目的,利用用户的操作习惯和数据更好的规划产品;2)作为用户ID来唯一识别用户,可以用游客身份使用app又能在服务器端保存相应的信息,省去用户名、密码等注册过程。

    那么,如何使用Mac地址生成设备的唯一标识呢?主要分三种:
    1、直接使用“MAC Address”
    2、使用“MD5(MAC Address)”
    3、使用“MD5(Mac Address+bundle_id)”获得“机器+应用”的唯一标识(bundle_id 是应用的唯一标识)


          iOS7之前,因为Mac地址是唯一的, 一般app开发者会采取第3种方式来识别安装对应app的设备。为什么会使用它?在iOS5之前,都是使用UDID的,后来被禁用。苹果推荐使用UUID 但是也有诸多问题,从而使用MAC地址。而MAC地址跟UDID一样,存在隐私问题,现在苹果新发布的iOS7上,如果请求Mac地址都会返回一个固定 值,那么Mac Address+bundle_id这个值大家的设备都变成一致的啦,跟UDID一样相当于被禁用。


    四.OPEN UDID

    OPEN UDID,没有用到MAC地址,同时能保证同一台设备上的不同应用使用同一个OpenUDID,只要用户设备上有一个使用了OpenUDID的应用存在时,其他后续安装的应用如果获取OpenUDID,都将会获得第一个应用生成的那个。但是根据贡献者的代码和方法,和一些开发者的经验,如果把使用了OpenUDID方案的应用全部都删除,再重新获取OpenUDID,此时的OpenUDID就跟以前的不一样。可见,这种方法还是不保险。(可通过重置手机功能直接修改)


    五.广告标示符(IDFA-identifierForIdentifier)

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


    六.Vindor标示符 (IDFV-identifierForVendor)

    Vindor标示符,也是在iOS 6中新增的,跟advertisingIdentifier一样,该方法返回的是一个 NSUUID对象,可以获得一个UUID。如果满足条件“相同的一个程序里面-相同的vindor-相同的设备”,那么获取到的这个属性值就不会变。如果是“相同的程序-相同的设备-不同的vindor,或者是相同的程序-不同的设备-无论是否相同的vindor”这样的情况,那么这个值是不会相同的。


    七.推送token+bundle_id

    推送token+bundle_id的方法:
    1、应用中增加推送用来获取token
    2、获取应用bundle_id
    3、根据token+bundle_id进行散列运算
    apple push token保证设备唯一,但必须有网络情况下才能工作,该方法不依赖于设备本身,但依赖于apple push,而苹果push有时候会抽风的。

    以上是主流的几种获取策略,差不多被苹果封禁的差不多了,下面讲解折中的办法:

    第一个解决的办法是:UUID一般只生成一次,保存在iOS系统里面,如果应用删除了,重装应用之后它的UUID还是一样的,除非系统重置 。但是不能保证在以后的系统升级后还能用(如果系统保存了该信息就能用).

    由于IOS系统存储的数据都是在sandBox里面,一旦删除App,sandBox也不复存在。好在有一个例外,那就是keychain(钥匙串)。

    通常情况下,IOS系统用NSUserDefaults存储数据信息,但是对于一些私密信息,比如密码、证书等等,就需要使用更为安全的keychain了。


    keychain里保存的信息不会因App被删除而丢失。所以,可以利用这个keychain这个特点来保存设备唯一标识。


    那么,如何在应用里使用使用keyChain呢,我们需要导入Security.framework ,keychain的操作接口声明在头文件SecItem.h里。


    直接使用SecItem.h里方法操作keychain,需要写的代码较为复杂,我们可以使用已经封装好了的工具类KeychainItemWrapper来对keychain进行操作。


    KeychainItemWrapper是apple官方例子“GenericKeychain”里一个访问keychain常用操作的封装类,在官网上下载了GenericKeychain项目后,


    只需要把“KeychainItemWrapper.h”和“KeychainItemWrapper.m”拷贝到我们项目,并导入Security.framework 。KeychainItemWrapper的用法:

    //获取手机的UUID(设备的唯一标示)
    -(NSString *)getDeviceIdentifier
    {
        KeychainItemWrapper *wrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@"deviceIdentifier" accessGroup:nil];
        NSString *uniqueIdentifier = [wrapper objectForKey:(id)kSecAttrAccount];
        NSLog(@"device_identifier:%@",uniqueIdentifier);
        NSLog(@"device_ADID:%@",[[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString]);
        
        //通过广告标识符 暂时替换(但使用ASI而并没有广告在上架的时候会被拒绝)
        if ([uniqueIdentifier isEqualToString:@""]) {
            [wrapper setObject:[[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString] forKey:(id)kSecAttrAccount];
            /*
            如果是上架问题也可以替换为别的
            [[UIDevice currentDevice].identifierForVendor UUIDString];
            */
            NSLog(@"set uniqueIdentifier.");
        }
        uniqueIdentifier = [wrapper objectForKey:(id)kSecAttrAccount];
        return uniqueIdentifier;
    }

     

    第二个解决办法(也是目前最常用的方法):使用描述文件获取手机的UDID

    让你的手机安装一个描述文件,就能在控制台获得手机的udid

    首先需要你新建一个后缀是.mobileconifg的这么一个文件,文件其实就是一个XML的文件

    需要修改是以上两个地方,第一个是你的后台的URL,描述文件安装的时候就会发送请求给这个URL,在URL的get请求内能获得设备的信息

    第二个地方是你所请求的设备信息UDID,Version等

    完成xml后 将文件放入服务器,在写一个php文件指向改文件即可

    receive.php

    <?php
    $data = file_get_contents('php://input');
    //这里可以进行xml解析
    //header("Location: http://dev.skyfox.org/udid?data=".rawurlencode($data)); //有人说必须得目录形式才会安装成功
    header('HTTP/1.1 301 Moved Permanently');  //这里一定要301跳转,否则设备安装会提示"无效的描述文件"
    header("Location: http://服务器地址/udid/index.php?".$params);
    ?>

    关于后续文件签名可以参考我的另一篇博文:https://blog.csdn.net/u012717715/article/details/86542360

    展开全文
  • 各种获取设备唯一标识的方法介绍, 实现获取唯一标识的最好的方法。

    各种获取设备唯一标识的方法介绍

    一.UDID(Unique Device Identifier)

    UDID的全称是Unique Device Identifier,它就是苹果iOS设备的唯一识别码,它由40位16进制数的字母和数字组成(越狱的设备通过某些工具可以改变设备的UDID)。移动网络可利用UDID来识别移动设备,但是,从IOS5.0(2011年8月份)开始,苹果宣布将不再支持用uniqueIdentifier方法获取设备的UDID,iOS5以下是可以用的。苹果从iOS5开始就移除了通过代码访问UDID的权限。从2013年5月1日起,试图访问UIDIDs的程序将不再被审核通过,替代的方案是开发者应该使用“在iOS 6中介绍的Vendor或Advertising标示符”。所以UDID是绝对是不能再使用了。

    //UUID , 已废除
    NSString *udid = [[UIDevice currentDevice] uniqueIdentifier];
    

    为什么苹果反对开发人员使用UDID?
    iOS 2.0版本以后UIDevice提供一个获取设备唯一标识符的方法uniqueIdentifier,通过该方法我们可以获取设备的序列号,这个也是目前为止唯一可以确认唯一的标示符。 许多开发者把UDID跟用户的真实姓名、密码、住址、其它数据关联起来;网络窥探者会从多个应用收集这些数据,然后顺藤摸瓜得到这个人的许多隐私数据。同时大部分应用确实在频繁传输UDID和私人信息。 为了避免集体诉讼,苹果最终决定在iOS 5 的时候,将这一惯例废除,开发者被引导生成一个唯一的标识符,只能检测应用程序,其他的信息不提供。现在应用试图获取UDID已被禁止且不允许上架。

    二.UUID(Universally Unique Identifier)

    UUID是Universally Unique Identifier的缩写,中文意思是通用唯一识别码。它是让分布式系统中的所有元素,都能有唯一的辨识信息,而不需要透过中央控制端来做辨识信息的指定。这样,每个人都可以建立不与其它人冲突的 UUID。在此情况下,就不需考虑数据库建立时的名称重复问题。苹果公司建议使用UUID为应用生成唯一标识字符串。
    获得的UUID值系统没有存储, 而且每次调用得到UUID,系统都会返回一个新的唯一标示符。如果你希望存储这个标示符,那么需要自己将其存储到NSUserDefaults, Keychain, Pasteboard或其它地方。

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

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

    获得的这个CFUUID值系统并没有存储。每次调用CFUUIDCreate,系统都会返回一个新的唯一标示符。如果你希望存储这个标示符,那么需要自己将其存储到NSUserDefaults, Keychain, Pasteboard或其它地方。

    ###2.NSUUID
    NSUUID在iOS 6中才出现,这跟CFUUID几乎完全一样,只不过它是Objective-C接口。+ (id)UUID 是一个类方法,调用该方法可以获得一个UUID。通过下面的代码可以获得一个UUID字符串:

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

    跟CFUUID一样,这个值系统也不会存储,每次调用的时候都会获得一个新的唯一标示符。如果要存储的话,你需要自己存储。在我读取NSUUID时,注意到获取到的这个值跟CFUUID完全一样(不过也可能不一样)

    三.open UDID

    在iOS 5发布时,uniqueIdentifier被弃用了,这引起了广大开发者需要寻找一个可以替代UDID,并且不受苹果控制的方案。由此OpenUDID成为了当时使用最广泛的开源UDID替代方案。OpenUDID在工程中实现起来非常简单,并且还支持一系列的广告提供商。

    OpenUDID利用了一个非常巧妙的方法在不同程序间存储标示符 — 在粘贴板中用了一个特殊的名称来存储标示符。通过这种方法,别的程序(同样使用了OpenUDID)知道去什么地方获取已经生成的标示符(而不用再生成一个新的)。而且根据贡献者的代码和方法,和一些开发者的经验,如果把使用了OpenUDID方案的应用全部都删除,再重新获取OpenUDID,此时的OpenUDID就跟以前的不一样。可见,这种方法还是不保险。
    但是OpenUDID库早已经弃用了, 在其官方的博客中也指明了, 停止维护OpenUDID的原因是为了更好的向苹果的举措靠拢, 还指明了MAC Address不是一个好的选择。

    四.MAC Address

    1.这个MAC地址是指什么?有什么用?

    MAC(Medium/Media Access Control)地址,用来表示互联网上每一个站点的标识符,采用十六进制数表示,共六个字节(48位)。其中,前三个字节是由IEEE的注册管理机构 RA负责给不同厂家分配的代码(高位24位),也称为“编制上唯一的标识符” (Organizationally Unique Identifier),后三个字节(低位24位)由各厂家自行指派给生产的适配器接口,称为扩展标识符(唯一性)。
    MAC地址在网络上用来区分设备的唯一性,接入网络的设备都有一个MAC地址,他们肯定都是不同的,是唯一的。一部iPhone上可能有多个MAC地址,包括WIFI的、SIM的等,但是iTouch和iPad上就有一个WIFI的,因此只需获取WIFI的MAC地址就好了,也就是en0的地址。
    形象的说,MAC地址就如同我们身份证上的身份证号码,具有全球唯一性。这样就可以非常好的标识设备唯一性,类似与苹果设备的UDID号,通常的用途有:
    1)用于一些统计与分析目的,利用用户的操作习惯和数据更好的规划产品;
    2)作为用户ID来唯一识别用户,可以用游客身份使用app又能在服务器端保存相应的信息,省去用户名、密码等注册过程。

    2.如何使用Mac地址生成设备的唯一标识呢?

    主要分三种:
    1、直接使用“MAC Address”
    2、使用“MD5(MAC Address)”
    3、使用“MD5(Mac Address+bundle_id)”获得“机器+应用”的唯一标识(bundle_id 是应用的唯一标识)

    iOS7之前,因为Mac地址是唯一的, 一般app开发者会采取第3种方式来识别安装对应app的设备。为什么会使用它?在iOS5之前,都是使用UDID的,后来被禁用。苹果推荐使用UUID 但是也有诸多问题,从而使用MAC地址。而MAC地址跟UDID一样,存在隐私问题,现在苹果新发布的iOS7上,如果请求Mac地址都会返回一个固定值,那么Mac Address+bundle_id这个值大家的设备都变成一致的啦,跟UDID一样相当于被禁用, 所以Mac Address 是不能够被使用为获取设备唯一标识的。

    五.广告标示符(IDFA-identifierForIdentifier)

    广告标示符,在同一个设备上的所有App都会取到相同的值,是苹果专门给各广告提供商用来追踪用户而设的。但好在Apple默认是允许追踪的,而且一般用户都不知道有这么个设置,所以基本上用来监测推广效果,是戳戳有余了。
    它是iOS 6中另外一个新的方法,提供了一个方法advertisingIdentifier,通过调用该方法会返回一个NSUUID实例,最后可以获得一个UUID,由系统存储着的。

    #import <AdSupport/AdSupport.h>
    	NSString *adId = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
    

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

    六.Vendor标示符 (IDFV-identifierForVendor)

    Vendor标示符,是给Vendor标识用户用的,每个设备在所属同一个Vender的应用里,都有相同的值。其中的Vender是指应用提供商,但准确点说,是通过BundleID的反转的前两部分进行匹配,如果相同就是同一个Vender,例如对于com.taobao.app1, com.taobao.app2 这两个BundleID来说,就属于同一个Vender,共享同一个IDFV的值。和IDFA不同的是,IDFV的值是一定能取到的,所以非常适合于作为内部用户行为分析的主id,来标识用户,替代OpenUDID。
    它是iOS 6中新增的,跟advertisingIdentifier一样,该方法返回的是一个 NSUUID对象,可以获得一个UUID。如果满足条件“相同的一个程序里面-相同的vendor-相同的设备”,那么获取到的这个属性值就不会变。如果是“相同的程序-相同的设备-不同的vendor,或者是相同的程序-不同的设备-无论是否相同的vendor”这样的情况,那么这个值是不会相同的。

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

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

    七.推送token+bundle_id

    推送token+bundle_id的方法:
    1、应用中增加推送用来获取token
    2、获取应用bundle_id
    3、根据token+bundle_id进行散列运算

    apple push token保证设备唯一,但必须有网络情况下才能工作,该方法并不是依赖于设备本身,而是依赖于apple push机制,所以当苹果push做出改变时, 你获取所谓的唯一标识也就随之失效了。所以此方法还是不可取的。

    八. NSUUID, CFUUID, IDFA, IDFV获取的标识对比

    首次运行

    NSUUID:9D820D3A-4429-4918-97F7-A69588B388A4
    CFUUID:80F961D0-1E6A-4ECD-A0A9-F58ED858FE20
    IDFA:687E6A90-50A3-4424-871C-BE255D050AFD
    IDFV:8E740A99-283B-4F6A-87EF-443FB7778488

    二次运行

    NSUUID:23AB8D3D-4F1D-45E2-8BD7-83B451125326
    CFUUID:14DDBFCF-67A6-46B7-BB48-4EF2ADC5429F
    IDFA:687E6A90-50A3-4424-871C-BE255D050AFD
    IDFV:8E740A99-283B-4F6A-87EF-443FB7778488

    卸载后, 重新安装运行

    NSUUID:BD934F9C-B7EC-4BD1-B65E-964C66537CAB
    CFUUID:29654DE0-AC93-40F9-98AB-1E10A271AF8D
    IDFA:687E6A90-50A3-4424-871C-BE255D050AFD
    IDFV:8E740A99-283B-4F6A-87EF-443FB7778488

    重启后运行

    NSUUID:82711557-3A17-4B82-8F18-09AADF9DD37B
    CFUUID:FFBC73EC-CFBE-414C-870E-77C0714E0347
    IDFA:687E6A90-50A3-4424-871C-BE255D050AFD
    IDFV:8E740A99-283B-4F6A-87EF-443FB7778488

    总结

    说了这么多, 才发现原来没有一种方法是可行的。没错, 其实自从苹果废除UDID后, 就不能达到获取设备真正的唯一标识了。因为这些方法中导致获取的唯一标示产生改变的原因, 或是重新调用方法, 或是重启设备, 或是卸载应用, 或是还原某些标识, 或者刷新系统…
    所以, 不能达到从根本上获取唯一标识, 我们只能做到尽可能接近。下面是我用过的方法。

    如何正确的获取设备的唯一标识

    我用的方法是将获取的UUID永久存储在设备的KeyChain中, 这个方法在应用第一次启动时, 将获取的UUID存储进KeyChain中, 每次取的时候, 检查本地钥匙串中有没有, 如果没有则需要将获取的UUID存储进去。当你重启设备, 卸载应用再次安装,都不影响, 只是当设备刷机时, KeyChain会清空, 才会消失, 才会失效。
    不只是这一种方法, 你也可以保存除UUID之外,其他合适的标识, 但利用KeyChain去存储标识的方式应该是最接近的。

    利用keyChain和UUID永久获得设备的唯一标识

    开发者可以在应用第一次启动时调用一次,然后将该串存储起来,以便以后替代UDID来使用。但是,如果用户删除该应用再次安装时,又会生成新的字符串,所以不能保证唯一识别该设备。这就需要各路高手想出各种解决方案。所以,之前很多应用就采用MAC Address。但是现在如果用户升级到iOS7(及其以后的苹果系统)后,他们机子的MAC Address就是一样的,没办法做区分,只能弃用此方法,重新使用UUID来标识。如果使用UUID,就要考虑应用被删除后再重新安装时的处理。

    什么是钥匙串?

    一、在应用间利用KeyChain共享数据
    我们可以把KeyChain理解为一个Dictionary,所有数据都以key-value的形式存储,可以对这个Dictionary进行add、update、get、delete这四个操作。对于每一个应用来说,KeyChain都有两个访问区,私有区和公共区。私有区是一个sandbox,本程序存储的任何数据都对其他程序不可见。而要想在将存储的内容放在公共区,需要先声明公共区的名称,官方文档管这个名称叫“keychain access group”,声明的方法是新建一个plist文件,名字随便起,内容如下:

    这里写图片描述

    “yourAppID.com.yourCompany.whatever”就是你要起的公共区名称,除了whatever字段可以随便定之外,其他的都必须如实填写。这个文件的路径要配置在 Project->build setting->Code Signing Entitlements里,否则公共区无效,配置好后,须用你正式的证书签名编译才可通过,否则xcode会弹框告诉你code signing有问题。所以,苹果限制了你只能同公司的产品共享KeyChain数据,别的公司访问不了你公司产品的KeyChain。

    二、保存私密信息
    iOS的keychain服务提供了一种安全的保存私密信息(密码,序列号,证书等)的方式,每个ios程序都有一个独立的keychain存储。相对于NSUserDefaults、文件保存等一般方式,keychain保存更为安全,而且keychain里保存的信息不会因App被删除而丢失,所以在重装App后,keychain里的数据还能使用。

    实现代码

    首先, 我先推荐两篇文章,里面介绍了如利用keyChain和UUID永久获得设备的唯一标识, Classes/KeychainItemWrapper.m如何使用KeyChain保存和获取UDID
    然后, 再介绍下我使用的方法以及封装的工具类, 在应用里使用使用keyChain,我们需要导入Security.framework。下面介绍下, 我在其他库基础上封装的一个获取唯一标识的工具类:

    <LZKeychain.h>

    #import <Foundation/Foundation.h>
    #import <Security/Security.h>
    
    NSString * const KEY_UDID_INSTEAD = @"com.myapp.udid.test";
    
    @interface LZKeychain : NSObject
    
    /**
     本方法是得到 UUID 后存入系统中的 keychain 的方法
     不用添加 plist 文件
     程序删除后重装,仍可以得到相同的唯一标示
     但是当系统升级或者刷机后,系统中的钥匙串会被清空,此时本方法失效
     */
    +(NSString *)getDeviceIDInKeychain;
    
    @end
    

    <LZKeychain.m>

    #import "LZKeychain.h"
    
    @implementation LZKeychain
    
    +(NSString *)getDeviceIDInKeychain
    {
        NSString *getUDIDInKeychain = (NSString *)[LZKeychain load:KEY_UDID_INSTEAD];
        NSLog(@"从keychain中获取到的 UDID_INSTEAD %@",getUDIDInKeychain);
        if (!getUDIDInKeychain ||[getUDIDInKeychain isEqualToString:@""]||[getUDIDInKeychain isKindOfClass:[NSNull class]]) {
            CFUUIDRef puuid = CFUUIDCreate( nil );
            CFStringRef uuidString = CFUUIDCreateString( nil, puuid );
            NSString * result = (NSString *)CFBridgingRelease(CFStringCreateCopy( NULL, uuidString));
            CFRelease(puuid);
            CFRelease(uuidString);
            NSLog(@"\n \n \n _____重新存储 UUID _____\n \n \n  %@",result);
            [LZKeychain save:KEY_UDID_INSTEAD data:result];
            getUDIDInKeychain = (NSString *)[LZKeychain load:KEY_UDID_INSTEAD];
        }
        NSLog(@"最终 ———— UDID_INSTEAD %@",getUDIDInKeychain);
        return getUDIDInKeychain;
    }
    
    #pragma mark - private
    
    + (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:(NSData *)keyData];
            } @catch (NSException *e) {
                NSLog(@"Unarchive of %@ failed: %@", service, e);
            } @finally {
            }
        }
        if (keyData)
            CFRelease(keyData);
        return ret;
    }
    
    + (void)delete:(NSString *)service {
        NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
        SecItemDelete((CFDictionaryRef)keychainQuery);
    }
    @end
    

    参考资料:
    The Developer’s Guide to Unique Identifiers
    获取设备的唯一标识

    展开全文
  • 免责声明:本文章来源于其他博客整理 参考:http://www.2cto.com/kf/201308/237648.html ... 在iOS系统中,获取设备唯一标识的方法有很多: 一.UDID(Unique Device Identifier) UDID的全称是Uniq

    免责声明:本文章来源于其他博客整理

    参考:http://www.2cto.com/kf/201308/237648.html

    参考:http://www.2cto.com/kf/201311/255684.html


    在iOS系统中,获取设备唯一标识的方法有很多:
    一.UDID(Unique Device Identifier)

    UDID的全称是Unique Device Identifier,它就是苹果IOS设备的唯一识别码,它由40个字符的字母和数字组成(越狱的设备通过某些工具可以改变设备的UDID)。移动网络可利用UDID来识别移动设备,但是,从IOS5.0(2011年8月份)开始,苹果宣布将不再支持用uniqueIdentifier方法获取设备的UDID,iOS5以下是可以用的。在2013年3月21日苹果已经通知开发者:从2013年5月1日起,访问UIDIDs的程序将不再被审核通过,替代的方案是开发者应该使用“在iOS 6中介绍的Vendor或Advertising标示符”。所以UDID是绝对不能用啦。



    二.UUID(Universally Unique Identifier)

    UUID是Universally Unique Identifier的缩写,中文意思是通用唯一识别码。它是让分布式系统中的所有元素,都能有唯一的辨识资讯,而不需要透过中央控制端来做辨识资讯的指定。这样,每个人都可以建立不与其它人冲突的 UUID。在此情况下,就不需考虑数据库建立时的名称重复问题。苹果公司建议使用UUID为应用生成唯一标识字符串。




    三.MAC Address

     这个MAC地址是指什么?有什么用?
           MAC(Medium/Media Access Control)地址,用来表示互联网上每一个站点的标识符,采用十六进制数表示,共六个字节(48位)。其中,前三个字节是由IEEE的注册管理机构 RA负责给不同厂家分配的代码(高位24位),也称为“编制上唯一的标识符” (Organizationally Unique Identifier),后三个字节(低位24位)由各厂家自行指派给生产的适配器接口,称为扩展标识符(唯一性)。
        MAC地址在网络上用来区分设备的唯一性,接入网络的设备都有一个MAC地址,他们肯定都是不同的,是唯一的。一部iPhone上可能有多个MAC地址,包括WIFI的、SIM的等,但是iTouch和iPad上就有一个WIFI的,因此只需获取WIFI的MAC地址就好了,也就是en0的地址。
          形象的说,MAC地址就如同我们身份证上的身份证号码,具有全球唯一性。这样就可以非常好的标识设备唯一性,类似与苹果设备的UDID号,通常的用途有:1)用于一些统计与分析目的,利用用户的操作习惯和数据更好的规划产品;2)作为用户ID来唯一识别用户,可以用游客身份使用app又能在服务器端保存相应的信息,省去用户名、密码等注册过程。


    那么,如何使用Mac地址生成设备的唯一标识呢?主要分三种:
    1、直接使用“MAC Address”
    2、使用“MD5(MAC Address)”
    3、使用“MD5(Mac Address+bundle_id)”获得“机器+应用”的唯一标识(bundle_id 是应用的唯一标识)


     iOS7之前,因为Mac地址是唯一的, 一般app开发者会采取第3种方式来识别安装对应app的设备。为什么会使用它?在iOS5之前,都是使用UDID的,后来被禁用。苹果推荐使用UUID 但是也有诸多问题,从而使用MAC地址。而MAC地址跟UDID一样,存在隐私问题,现在苹果新发布的iOS7上,如果请求Mac地址都会返回一个固定 值,那么Mac Address+bundle_id这个值大家的设备都变成一致的啦,跟UDID一样相当于被禁用。那么,要怎么标识设备唯一呢?

    关于为什么用mac地址作为手机的唯一标识,请参考知乎讨论



    四.OPEN UDID

    OPEN UDID,没有用到MAC地址,同时能保证同一台设备上的不同应用使用同一个OpenUDID,只要用户设备上有一个使用了OpenUDID的应用存在时,其他后续安装的应用如果获取OpenUDID,都将会获得第一个应用生成的那个。但是根据贡献者的代码和方法,和一些开发者的经验,如果把使用了OpenUDID方案的应用全部都删除,再重新获取OpenUDID,此时的OpenUDID就跟以前的不一样。可见,这种方法还是不保险。



    五.广告标示符(IDFA-identifierForIdentifier)

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

     




    六.Vindor标示符 (IDFV-identifierForVendor)

    Vindor标示符,也是在iOS 6中新增的,跟advertisingIdentifier一样,该方法返回的是一个 NSUUID对象,可以获得一个UUID。如果满足条件“相同的一个程序里面-相同的vindor-相同的设备”,那么获取到的这个属性值就不会变。如果是“相同的程序-相同的设备-不同的vindor,或者是相同的程序-不同的设备-无论是否相同的vindor”这样的情况,那么这个值是不会相同的。



    七.推送token+bundle_id


    推送token+bundle_id的方法:
    1、应用中增加推送用来获取token
    2、获取应用bundle_id
    3、根据token+bundle_id进行散列运算


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


    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    iOS中获取UUID的代码如下: 
    -(NSString*)uuid{  
        CFUUIDRef puuid = CFUUIDCreate( nil );  
        CFStringRef uuidString = CFUUIDCreateString( nil, puuid );  
        NSString * result = (NSString *)CFStringCreateCopy( NULL, uuidString);  
        CFRelease(puuid);  
        CFRelease(uuidString);  
        return[result autorelease];  
    iOS中获取网卡mac的代码如下:
    #include <sys/socket.h> // Per msqr 
    #include <sys/sysctl.h> 
    #include <net/if.h> 
    #include <net/if_dl.h> 
       
    #pragma mark MAC addy 
    // Return the local MAC addy 
    // Courtesy of FreeBSD hackers email list 
    // Accidentally munged during previous update. Fixed thanks to mlamb. 
    - (NSString *) macaddress
    {
        int                   mib[6];
        size_t               len;
        char               *buf;
        unsignedchar       *ptr;
        structif_msghdr    *ifm;
        structsockaddr_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");
            returnNULL;
        }
          
        if(sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
            printf("Error: sysctl, take 1/n");
            returnNULL;
        }
          
        if((buf = malloc(len)) == NULL) {
            printf("Could not allocate memory. error!/n");
            returnNULL;
        }
          
        if(sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
            printf("Error: sysctl, take 2");
            returnNULL;
        }
          
        ifm = (structif_msghdr *)buf;
        sdl = (structsockaddr_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)]; 
        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];
    }
     
    iOS中获取UUID的代码如下:
    -(NSString*)uuid{
        CFUUIDRef puuid = CFUUIDCreate( nil );
        CFStringRef uuidString = CFUUIDCreateString( nil, puuid );
        NSString * result = (NSString *)CFStringCreateCopy( NULL, uuidString);
        CFRelease(puuid);
        CFRelease(uuidString);
        return[result autorelease];
    }
    iOS中获取网卡mac的代码如下:
    #include <sys/socket.h> // Per msqr
    #include <sys/sysctl.h>
    #include <net/if.h>
    #include <net/if_dl.h>
      
    #pragma mark MAC addy
    // Return the local MAC addy
    // Courtesy of FreeBSD hackers email list
    // Accidentally munged during previous update. Fixed thanks to mlamb.
    - (NSString *) macaddress
    {
        int                   mib[6];
        size_t               len;
        char               *buf;
        unsignedchar       *ptr;
        structif_msghdr    *ifm;
        structsockaddr_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");
            returnNULL;
        }
        
        if(sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
            printf("Error: sysctl, take 1/n");
            returnNULL;
        }
        
        if((buf = malloc(len)) == NULL) {
            printf("Could not allocate memory. error!/n");
            returnNULL;
        }
        
        if(sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
            printf("Error: sysctl, take 2");
            returnNULL;
        }
        
        ifm = (structif_msghdr *)buf;
        sdl = (structsockaddr_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)];
        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];
    }

    八,利用keyChain和UUID永久获得设备的唯一标识

    开发者可以在应用第一次启动时调用一 次,然后将该串存储起来,以便以后替代UDID来使用。但是,如果用户删除该应用再次安装时,又会生成新的字符串,所以不能保证唯一识别该设备。这就需要各路高手想出各种解决方案。所以,之前很多应用就采用MAC Address。但是现在如果用户升级到iOS7(及其以后的苹果系统)后,他们机子的MAC Address就是一样的,没办法做区分,只能弃用此方法,重新使用UUID来标识。如果使用UUID,就要考虑应用被删除后再重新安装时的处理。

    那么什么是钥匙串呢:

    一、在应用间利用KeyChain共享数据

           我们可以把KeyChain理解为一个Dictionary,所有数据都以key-value的形式存储,可以对这个Dictionary进行add、update、get、delete这四个操作。对于每一个应用来说,KeyChain都有两个访问区,私有区和公共区。私有区是一个sandbox,本程序存储的任何数据都对其他程序不可见。而要想在将存储的内容放在公共区,需要先声明公共区的名称,官方文档管这个名称叫“keychain access group”,声明的方法是新建一个plist文件,名字随便起,内容如下:
     
     
    “yourAppID.com.yourCompany.whatever”就是你要起的公共区名称,除了whatever字段可以随便定之外,其他的都必须如实填写。这个文件的路径要配置在 Project->build setting->Code Signing Entitlements里,否则公共区无效,配置好后,须用你正式的证书签名编译才可通过,否则xcode会弹框告诉你code signing有问题。所以,苹果限制了你只能同公司的产品共享KeyChain数据,别的公司访问不了你公司产品的KeyChain。
     
    二、保存私密信息
    iOS的keychain服务提供了一种安全的保存私密信息(密码,序列号,证书等)的方式,每个ios程序都有一个独立的keychain存储。相对于NSUserDefaults、文件保存等一般方式,keychain保存更为安全,而且keychain里保存的信息不会因App被删除而丢失,所以在重装App后,keychain里的数据还能使用。
     
    在应用里使用使用keyChain,我们需要导入Security.framework ,keychain的操作接口声明在头文件SecItem.h里。直接使用SecItem.h里方法操作keychain,需要写的代码较为复杂,为减轻咱们程序员的开发,我们可以使用一些已经封装好了的工具类,下面我会简单介绍下我用过的两个工具类:KeychainItemWrapper和SFHFKeychainUtils。
    官方的demo   KeychainItemWrapper貌似不行
    那么我给我自己的解决方案

    CMUUIDManager.h文件
    @interface CMUUIDManager : NSObject
    
    +(void)saveUUID:(NSString *)uuid;
    
    +(id)readUUID;
    
    +(void)deleteUUID;
    
    @end

    CMUUIDManager.m文件
    #import "CMUUIDManager.h"
    #import "CMKeyChain.h"
    
    @implementation CMUUIDManager
    
    static NSString * const KEY_IN_KEYCHAIN = @"项目的Bundle ID";
    static NSString * const KEY_UUID = @"项目的BundleID.uuid";
    
    +(void)saveUUID:(NSString *)uuid
    {
        NSMutableDictionary *usernameUuidPairs = [NSMutableDictionary dictionary];
        [usernameUuidPairs setObject:uuid forKey:KEY_UUID];
        [CMKeyChain save:KEY_IN_KEYCHAIN data:usernameUuidPairs];
    }
    
    +(id)readUUID
    {
        NSMutableDictionary *usernameUuidPairs = (NSMutableDictionary *)[CMKeyChain load:KEY_IN_KEYCHAIN];
        return [usernameUuidPairs objectForKey:KEY_UUID];
    }
    
    +(void)deleteUUID
    {
        [CMKeyChain delete:KEY_IN_KEYCHAIN];
    }
    @end

    CMKeyChain.h文件
    @interface CMKeyChain : NSObject
    + (NSMutableDictionary *)getKeychainQuery:(NSString *)service ;
    
    + (void)save:(NSString *)service data:(id)data;
    
    + (id)load:(NSString *)service;
    
    + (void)delete:(NSString *)service;
    
    @end

    CMKeyChain.m文件
    #import "CMKeyChain.h"
    
    @implementation CMKeyChain
    + (NSMutableDictionary *)getKeychainQuery:(NSString *)service {
        return [NSMutableDictionary dictionaryWithObjectsAndKeys:
                (__bridge_transfer id)kSecClassGenericPassword,(__bridge_transfer id)kSecClass,
                service, (__bridge_transfer id)kSecAttrService,
                service, (__bridge_transfer id)kSecAttrAccount,
                (__bridge_transfer id)kSecAttrAccessibleAfterFirstUnlock,(__bridge_transfer 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
        CFDictionaryRef aRef = (__bridge_retained CFDictionaryRef)keychainQuery;
        SecItemDelete(aRef/*(__bridge_retained CFDictionaryRef)keychainQuery*/);
        //Add new object to search dictionary(Attention:the data format)
        [keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(__bridge_transfer id)kSecValueData];
        //Add item to keychain with the search dictionary
        SecItemAdd(aRef/*(__bridge_retained CFDictionaryRef)keychainQuery*/, NULL);
    }
    
    + (id)load:(NSString *)service {
        id ret = nil;
        NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
        //Configure the search setting
        [keychainQuery setObject:(id)kCFBooleanTrue forKey:(__bridge_transfer id)kSecReturnData];
        [keychainQuery setObject:(__bridge_transfer id)kSecMatchLimitOne forKey:(__bridge_transfer id)kSecMatchLimit];
        CFDataRef keyData = NULL;
        if (SecItemCopyMatching((__bridge_retained CFDictionaryRef)keychainQuery, (CFTypeRef *)&keyData) == noErr) {
            @try {
                ret = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge_transfer NSData *)keyData];
            } @catch (NSException *e) {
                NSLog(@"Unarchive of %@ failed: %@", service, e);
            } @finally {
            }
        }
        return ret;
    }
    
    + (void)delete:(NSString *)service {
        NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
        SecItemDelete((__bridge_retained CFDictionaryRef)keychainQuery);
    }
    @end
    

    调用在Appdelegate.m文件
        //获得UUID存入keyChain中
        NSUUID*UUID=[UIDevice currentDevice].identifierForVendor;
        NSString*uuid=[CMUUIDManager readUUID];
        NSLog(@"uuid==%@",uuid);
        if (uuid==nil) {
            [CMUUIDManager deleteUUID];
            [CMUUIDManager saveUUID:UUID.UUIDString];
        }
    

    然后随便在哪个文件中取值就可以了,现在就是唯一了
        NSString*uuid=[CMUUIDManager readUUID];
    





    展开全文
  • 各种获取设备唯一标识的方法介绍 一.UDID(Unique Device Identifier) UDID的全称是Unique Device Identifier,它就是苹果iOS设备的唯一识别码,它由40位16进制数的字母和数字组成(越狱的设备通过某些工具可以改变...

    转自:https://www.jianshu.com/p/686958c352f1
     

     

    各种获取设备唯一标识的方法介绍

    一.UDID(Unique Device Identifier)

    UDID的全称是Unique Device Identifier,它就是苹果iOS设备的唯一识别码,它由40位16进制数的字母和数字组成(越狱的设备通过某些工具可以改变设备的UDID)。移动网络可利用UDID来识别移动设备,但是,从IOS5.0(2011年8月份)开始,苹果宣布将不再支持用uniqueIdentifier方法获取设备的UDID,iOS5以下是可以用的。苹果从iOS5开始就移除了通过代码访问UDID的权限。从2013年5月1日起,试图访问UIDIDs的程序将不再被审核通过,替代的方案是开发者应该使用“在iOS 6中介绍的Vendor或Advertising标示符”。所以UDID是绝对是不能再使用了.

    //UUID , 已废除
    NSString *udid = [[UIDevice currentDevice] uniqueIdentifier];
    

    为什么苹果反对开发人员使用UDID?
    iOS 2.0版本以后UIDevice提供一个获取设备唯一标识符的方法uniqueIdentifier,通过该方法我们可以获取设备的序列号,这个也是目前为止唯一可以确认唯一的标示符。 许多开发者把UDID跟用户的真实姓名、密码、住址、其它数据关联起来;网络窥探者会从多个应用收集这些数据,然后顺藤摸瓜得到这个人的许多隐私数据。同时大部分应用确实在频繁传输UDID和私人信息。 为了避免集体诉讼,苹果最终决定在iOS 5 的时候,将这一惯例废除,开发者被引导生成一个唯一的标识符,只能检测应用程序,其他的信息不提供。现在应用试图获取UDID已被禁止且不允许上架。


    二.UUID(Universally Unique Identifier)

    UUID是Universally Unique Identifier的缩写,中文意思是通用唯一识别码。它是让分布式系统中的所有元素,都能有唯一的辨识资讯,而不需要透过中央控制端来做辨识资讯的指定。这样,每个人都可以建立不与其它人冲突的 UUID。在此情况下,就不需考虑数据库建立时的名称重复问题。苹果公司建议使用UUID为应用生成唯一标识字符串。
    获得的UUID值系统没有存储, 而且每次调用得到UUID,系统都会返回一个新的唯一标示符。如果你希望存储这个标示符,那么需要自己将其存储到NSUserDefaults, Keychain, Pasteboard或其它地方。

    • CFUUID
      从iOS2.0开始,CFUUID就已经出现了。它是CoreFoundatio包的一部分,因此API属于C语言风格。CFUUIDCreate 方法用来创建CFUUIDRef,并且可以获得一个相应的NSString,如下代码:
    CFUUIDRef cfuuid = CFUUIDCreate(kCFAllocatorDefault);
    NSString *cfuuidString = (NSString*)CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, cfuuid));
    

    获得的这个CFUUID值系统并没有存储。每次调用CFUUIDCreate,系统都会返回一个新的唯一标示符。如果你希望存储这个标示符,那么需要自己将其存储到NSUserDefaults, Keychain, Pasteboard或其它地方。

    • NSUUID
      NSUUID在iOS 6中才出现,这跟CFUUID几乎完全一样,只不过它是Objective-C接口。+ (id)UUID 是一个类方法,调用该方法可以获得一个UUID。通过下面的代码可以获得一个UUID字符串:
    NSString *uuid = [[NSUUID UUID] UUIDString];
    

    跟CFUUID一样,这个值系统也不会存储,每次调用的时候都会获得一个新的唯一标示符。如果要存储的话,你需要自己存储。在我读取NSUUID时,注意到获取到的这个值跟CFUUID完全一样(不过也可能不一样


    三.Open UDID

    在iOS 5发布时,uniqueIdentifier被弃用了,这引起了广大开发者需要寻找一个可以替代UDID,并且不受苹果控制的方案。由此OpenUDID成为了当时使用最广泛的开源UDID替代方案。OpenUDID在工程中实现起来非常简单,并且还支持一系列的广告提供商。

    OpenUDID利用了一个非常巧妙的方法在不同程序间存储标示符 — 在粘贴板中用了一个特殊的名称来存储标示符。通过这种方法,别的程序(同样使用了OpenUDID)知道去什么地方获取已经生成的标示符(而不用再生成一个新的)。而且根据贡献者的代码和方法,和一些开发者的经验,如果把使用了OpenUDID方案的应用全部都删除,再重新获取OpenUDID,此时的OpenUDID就跟以前的不一样。可见,这种方法还是不保险。

    但是OpenUDID库早已经弃用了, 在其官方的博客中也指明了, 停止维护OpenUDID的原因是为了更好的向苹果的举措靠拢, 还指明了MAC Address不是一个好的选择。


    四.MAC Address

    • 这个MAC地址是指什么?有什么用?
      MAC(Medium/Media Access Control)地址,用来表示互联网上每一个站点的标识符,采用十六进制数表示,共六个字节(48位)。其中,前三个字节是由IEEE的注册管理机构 RA负责给不同厂家分配的代码(高位24位),也称为“编制上唯一的标识符” (Organizationally Unique Identifier),后三个字节(低位24位)由各厂家自行指派给生产的适配器接口,称为扩展标识符(唯一性)。

    MAC地址在网络上用来区分设备的唯一性,接入网络的设备都有一个MAC地址,他们肯定都是不同的,是唯一的。一部iPhone上可能有多个MAC地址,包括WIFI的、SIM的等,但是iTouch和iPad上就有一个WIFI的,因此只需获取WIFI的MAC地址就好了,也就是en0的地址。

    形象的说,MAC地址就如同我们身份证上的身份证号码,具有全球唯一性。这样就可以非常好的标识设备唯一性,类似与苹果设备的UDID号,通常的用途有:
    (1) 用于一些统计与分析目的,利用用户的操作习惯和数据更好的规划产品;
    (2) 作为用户ID来唯一识别用户,可以用游客身份使用app又能在服务器端保存相应的信息,省去用户名、密码等注册过程。

    • 如何使用Mac地址生成设备的唯一标识呢?
      主要分三种:
      1、直接使用“MAC Address”
      2、使用“MD5(MAC Address)”
      3、使用“MD5(Mac Address+bundle_id)”获得“机器+应用”的唯一标识(bundle_id 是应用的唯一标识)

    iOS7之前,因为Mac地址是唯一的, 一般app开发者会采取第3种方式来识别安装对应app的设备。为什么会使用它?在iOS5之前,都是使用UDID的,后来被禁用。苹果推荐使用UUID 但是也有诸多问题,从而使用MAC地址。而MAC地址跟UDID一样,存在隐私问题,现在苹果新发布的iOS7上,如果请求Mac地址都会返回一个固定值,那么Mac Address+bundle_id这个值大家的设备都变成一致的啦,跟UDID一样相当于被禁用, 所以Mac Address 是不能够被使用为获取设备唯一标识的。


    五.广告标示符(IDFA-identifierForIdentifier)

    广告标示符,在同一个设备上的所有App都会取到相同的值,是苹果专门给各广告提供商用来追踪用户而设的。但好在Apple默认是允许追踪的,而且一般用户都不知道有这么个设置,所以基本上用来监测推广效果,是戳戳有余了。

    它是iOS 6中另外一个新的方法,提供了一个方法advertisingIdentifier,通过调用该方法会返回一个NSUUID实例,最后可以获得一个UUID,由系统存储着的。

    #import <AdSupport/AdSupport.h>
    NSString *adId = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
    

    不过即使这是由系统存储的,但是有几种情况下,会重新生成广告标示符。
    (1)如果用户完全重置系统((设置程序 -> 通用 -> 还原 -> 还原位置与隐私) ,这个广告标示符会重新生成。
    (2)另外如果用户明确的还原广告(设置程序-> 通用 -> 关于本机 -> 广告 -> 还原广告标示符) ,那么广告标示符也会重新生成。

    关于广告标示符的还原,有一点需要注意:如果程序在后台运行,此时用户“还原广告标示符”,然后再回到程序中,此时获取广 告标示符并不会立即获得还原后的标示符。必须要终止程序,然后再重新启动程序,才能获得还原后的广告标示符。

    所以IDFA也不可以作为获取唯一标识的方法,来识别用户。


    六.Vendor标示符 (IDFV-identifierForVendor)

    Vendor标示符,是给Vendor标识用户用的,每个设备在所属同一个Vender的应用里,都有相同的值。其中的Vender是指应用提供商,但准确点说,是通过BundleID的反转的前两部分进行匹配,如果相同就是同一个Vender,例如对于com.taobao.app1, com.taobao.app2 这两个BundleID来说,就属于同一个Vender,共享同一个IDFV的值。和IDFA不同的是,IDFV的值是一定能取到的,所以非常适合于作为内部用户行为分析的主id,来标识用户,替代OpenUDID。

    它是iOS 6中新增的,跟advertisingIdentifier一样,该方法返回的是一个 NSUUID对象,可以获得一个UUID。如果满足条件“相同的一个程序里面-相同的vendor-相同的设备”,那么获取到的这个属性值就不会变。如果是“相同的程序-相同的设备-不同的vendor,或者是相同的程序-不同的设备-无论是否相同的vendor”这样的情况,那么这个值是不会相同的。

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

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


    七.推送token+bundle_id

    推送token+bundle_id的方法:
    1、应用中增加推送用来获取token
    2、获取应用bundle_id
    3、根据token+bundle_id进行散列运算

    apple push token保证设备唯一,但必须有网络情况下才能工作,该方法并不是依赖于设备本身,而是依赖于apple push机制,所以当苹果push做出改变时, 你获取所谓的唯一标识也就随之失效了。所以此方法还是不可取的。


    总结

    说了这么多, 才发现原来没有一种方法是可行的。没错, 其实自从苹果废除UDID后, 就不能达到获取设备真正的唯一标识了。因为这些方法中导致获取的唯一标示产生改变的原因, 或是重新调用方法, 或是重启设备, 或是卸载应用, 或是还原某些标识, 或者刷新系统…
    所以, 不能达到从根本上获取唯一标识, 我们只能做到尽可能接近。下面是我用过的方法。


    如何正确的获取设备的唯一标识

    我用的方法是将获取的UUID永久存储在设备的KeyChain中, 这个方法在应用第一次启动时, 将获取的UUID存储进KeyChain中, 每次取的时候, 检查本地钥匙串中有没有, 如果没有则需要将获取的UUID存储进去。当你重启设备, 卸载应用再次安装,都不影响, 只是当设备刷机时, KeyChain会清空, 才会消失, 才会失效。
    不只是这一种方法, 你也可以保存除UUID之外,其他合适的标识, 但利用KeyChain去存储标识的方式应该是最接近的。


    利用keyChain和UUID永久获得设备的唯一标识

    开发者可以在应用第一次启动时调用一次,然后将该串存储起来,以便以后替代UDID来使用。但是,如果用户删除该应用再次安装时,又会生成新的字符串,所以不能保证唯一识别该设备。这就需要各路高手想出各种解决方案。所以,之前很多应用就采用MAC Address。但是现在如果用户升级到iOS7(及其以后的苹果系统)后,他们机子的MAC Address就是一样的,没办法做区分,只能弃用此方法,重新使用UUID来标识。如果使用UUID,就要考虑应用被删除后再重新安装时的处理。


    什么是钥匙串?

    • 在应用间利用KeyChain共享数据

    我们可以把KeyChain理解为一个Dictionary,所有数据都以key-value的形式存储,可以对这个Dictionary进行add、update、get、delete这四个操作。对于每一个应用来说,KeyChain都有两个访问区,私有区和公共区。私有区是一个sandbox,本程序存储的任何数据都对其他程序不可见。而要想在将存储的内容放在公共区,需要先声明公共区的名称,官方文档管这个名称叫“keychain access group”,声明的方法是新建一个plist文件,名字随便起,内容如下

    plist.png

    “yourAppID.com.yourCompany.whatever”就是你要起的公共区名称,除了whatever字段可以随便定之外,其他的都必须如实填写。这个文件的路径要配置在 Project->build setting->Code Signing Entitlements里,否则公共区无效,配置好后,须用你正式的证书签名编译才可通过,否则xcode会弹框告诉你code signing有问题。所以,苹果限制了你只能同公司的产品共享KeyChain数据,别的公司访问不了你公司产品的KeyChain。

    • 保存私密信息

    iOS的keychain服务提供了一种安全的保存私密信息(密码,序列号,证书等)的方式,每个ios程序都有一个独立的keychain存储。相对于NSUserDefaults、文件保存等一般方式,keychain保存更为安全,而且keychain里保存的信息不会因App被删除而丢失,所以在重装App后,keychain里的数据还能使用。


    实现代码

    介绍下我使用的方法以及封装的工具类, 在应用里使用使用keyChain,我们需要导入Security.framework。下面介绍下, 我在其他库基础上封装的一个获取唯一标识的工具类:

    #import <Foundation/Foundation.h>
    #import <Security/Security.h>
    
    @interface BGKeychainTool : NSObject
    
    /**
     本方法是得到 UUID 后存入系统中的 keychain 的方法
     不用添加 plist 文件
     程序删除后重装,仍可以得到相同的唯一标示
     但是当系统升级或者刷机后,系统中的钥匙串会被清空,此时本方法失效
     */
    +(NSString *)getDeviceIDInKeychain;
    
    @end
    

    .m 文件实现

    #import "BGKeychainTool.h"
    
    NSString * const KEY_UDID_INSTEAD = @"com.myapp.udid.test";
    
    @implementation BGKeychainTool
    
    +(NSString *)getDeviceIDInKeychain {
        NSString *getUDIDInKeychain = (NSString *)[BGKeychainTool load:KEY_UDID_INSTEAD];
        NSLog(@"从keychain中获取到的 UDID_INSTEAD %@",getUDIDInKeychain);
        if (!getUDIDInKeychain ||[getUDIDInKeychain isEqualToString:@""]||[getUDIDInKeychain isKindOfClass:[NSNull class]]) {
            CFUUIDRef puuid = CFUUIDCreate( nil );
            CFStringRef uuidString = CFUUIDCreateString( nil, puuid );
            NSString * result = (NSString *)CFBridgingRelease(CFStringCreateCopy( NULL, uuidString));
            CFRelease(puuid);
            CFRelease(uuidString);
            NSLog(@"\n \n \n _____重新存储 UUID _____\n \n \n  %@",result);
            [BGKeychainTool save:KEY_UDID_INSTEAD data:result];
            getUDIDInKeychain = (NSString *)[BGKeychainTool load:KEY_UDID_INSTEAD];
        }
        NSLog(@"最终 ———— UDID_INSTEAD %@",getUDIDInKeychain);
        return getUDIDInKeychain;
    }
    

    下面有几个私有方法

    • 通过 key 获取数据
    + (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)delete:(NSString *)service {
        NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
        SecItemDelete((CFDictionaryRef)keychainQuery);
    }
    

    首次编译

     

    编译结果

    再次编译

    编译结果.png

    再次非常感谢该作者,本文大部分参考116.iOS获取设备的唯一标识的方法总结以及最好的方法.

    参考资料

    如何使用KeyChain保存和获取UDID
    The Developer’s Guide to Unique Identifiers
    获取设备的唯一标识

    感言

    乐于分享.乐于奉献.一起进步,生活更美好

     

    展开全文
  • 获取iOS设备唯一标示

    2016-08-16 10:35:25
    在产品的需求中,很多情况下我们需要获取设备唯一标示,那么获取设备唯一标示的方式有哪几种呢,在这里做一下总结: 我们通常都说UDID、IDFA、UUID等,获取苹果设备唯一标示; UDID 是由子母和数字组成的40...
  • 开发中有时候我们需要获取设备的唯一标识来处理一些业务上的需求,在iOS5版本之前,我们可以通过...现在给大家推荐一个获取设备唯一标识的方法,NSUUID+KeyChain的方法: 首先获取设备的UUID:NSString *UUID = [[NSUU
  • 在之前的版本是可以使用UDID获取iOS设备唯一标识, NSString *udidString = [[UIDevice currentDevice] uniqueIdentifier]; 但是iOS5及以后,被苹果禁止使用了(弃用了) 而直接获取的UUID系统不会存储,每次...
  • 那么在开发中如何才能标识设备唯一性呢?apple公司提供的方法是通过keychain来存一些标志信息,然后通过存的标志信息来让应用程序来识别该设备唯一性。  apple公司写了一个简单的操作keychain的工具类:...
  • 在之前的版本是可以被使用的,iOS5及以后,被苹果禁止使用了(弃用了)。虽然,这个UDID用得很广泛,但是,不得不说的是,它在慢慢的远离开发者,不能在考虑使用UDID了。至于这个标示符是转为私有方法,或者完全从...
  • 当我们使用CoreBluetooth系统框架进行蓝牙开发的时候,有时因为某种功能需求需要拿到特定的蓝牙设备进行特定的操作,这就需要我们拿到能够代表特定的蓝牙设备唯一标识,通过唯一标识来确认是哪一台蓝牙设备
  • 很多地方都会需要用到唯一标志。 比如: 1. 我们相用一个设备唯一标志当作用户id,特别是网络游戏,这样就可以省去注册的麻烦。 2. 想把app相关的文件加密,密钥哪里来的?有些人可能会说hard code,但是hard ...
  •  iOS 7中苹果再一次无情的封杀mac地址,现在...apple公司提供的方法是通过keychain来存一些标志信息,然后通过存的标志信息来让应用程序来识别该设备唯一性。  apple公司写了一个简单的操作keychain的
  • -[UIDevice uniqueIdentifier]在iOS5实际在iOS5的时候已经被遗弃了,但是iOS7中已经完全的禁用了它。Xcode5甚至不会允许你编译包含了指引到-[UIDevice uniqueIdentifier]的app。此外,iOS7之前的使用了-[UIDevice
  • -[UIDevice uniqueIdentifier]在iOS5实际在iOS5的时候已经被遗弃了,但是iOS7中已经完全的禁用了它。 Xcode5甚至不会允许你编译包含了指引到-[UIDevice uniqueIdentifier]的app。 此外,iOS7之前的使用了-[UIDevice ...
  • 由于iOS7禁用了设备唯一号的标识,所以重新安装app时就会前后两次获取不一样的标示符, iOS系统存储的数据都是在sandBox沙盒里面,一旦删除app.沙盒也被删除.但是有一个例外的是keychain(钥匙串), 一般情况下,iOS...
1 2 3 4 5 ... 20
收藏数 13,629
精华内容 5,451