关于iOS:uidevice uniqueidentifier已弃用-现在该怎么办?

UIDevice uniqueIdentifier deprecated - What to do now?

刚刚发现iOS 5中不推荐使用uidevice uniqueidentifier属性,iOS 7及更高版本中不可用。似乎没有其他方法或属性可用或即将可用。

我们现有的许多应用程序都严格依赖于此属性来唯一标识特定设备。我们如何处理这个问题?

2011-2012年文件中的建议是:

Special Considerations

Do not use the uniqueIdentifier property. To create a unique identifier specific
to your app, you can call the CFUUIDCreate function to create a UUID, and write
it to the defaults database using the NSUserDefaults class.

但是,如果用户卸载并重新安装应用程序,该值将不相同。


如果用户卸载并重新安装应用程序,那么由CFUUIDCreate创建的uuid是唯一的:您每次都会得到一个新的。

但是您可能希望它不是唯一的,也就是说,当用户卸载并重新安装应用程序时,它应该保持不变。这需要一些努力,因为每个设备最可靠的标识符似乎是MAC地址。您可以查询mac并将其用作uuid。

编辑:当然,需要始终查询同一接口的MAC。我想最好的办法是和en0打交道。MAC始终存在,即使接口没有IP/已关闭。

编辑2:正如其他人所指出的,iOS 6以来的首选解决方案是-[uidevice identifier for vendor]。在大多数情况下,您应该可以将其作为旧-[UIDevice uniqueIdentifier]的替代品(但当应用程序首次启动时创建的uuid似乎是苹果希望您使用的)。

edit 3:so this major point does not get lost in the comment noise:do not use the mac as uuid,create a hash using the mac.这个哈希每次都会创建相同的结果,即使是在重新安装和应用程序之间(如果哈希是以相同的方式完成的)。无论如何,现在(2013年),这不再是必要的,除非你在iOS上需要一个"稳定"的设备标识符<6.0.

编辑4:在iOS7中,苹果在查询Mac时总是返回一个固定的值,以专门阻止Mac作为ID方案的基础。所以您现在真的应该使用-[uidevice identifierforvendor]或创建一个每安装UUID。


你已经可以用你的替代品来代替苹果的UDID。好心的盖伊·格基茨在UIDevice上写了类别,它将根据设备MAC地址和包标识符生成某种UDID

你可以在Github上找到代码


基于@mouth提出的链接,我做了几个测试,这似乎是最好的解决方案。正如@darkdust所说,该方法用于检查en0,它始终可用。有两种选择:uniqueDeviceIdentifier(MAC+CFbundleIdentifier的MD5)和uniqueGlobalDeviceIdentifier(mac的md5),它们总是返回相同的值。下面是我做的测试(使用真实设备):

1
2
3
4
#import"UIDevice+IdentifierAddition.h"

NSLog(@"%@",[[UIDevice currentDevice] uniqueDeviceIdentifier]);
NSLog(@"%@",[[UIDevice currentDevice] uniqueGlobalDeviceIdentifier]);

XXXX21f1f19edff198e2a2356bf4XXXX - (WIFI)UDID
XXXX7dc3c577446a2bcbd77935bdXXXX - (WIFI)GlobalAppUDID

XXXX21f1f19edff198e2a2356bf4XXXX - (3G)UDID
XXXX7dc3c577446a2bcbd77935bdXXXX - (3G)GlobalAppUDID

XXXX21f1f19edff198e2a2356bf4XXXX - (GPRS)UDID
XXXX7dc3c577446a2bcbd77935bdXXXX - (GPRS)GlobalAppUDID

XXXX21f1f19edff198e2a2356bf4XXXX - (AirPlane mode)UDID
XXXX7dc3c577446a2bcbd77935bdXXXX - (AirPlane mode)GlobalAppUDID

XXXX21f1f19edff198e2a2356bf4XXXX - (Wi-Fi)after removing and
reinstalling the app XXXX7dc3c577446a2bcbd77935bdXXXX (Wi-Fi) after
removing and installing the app

希望它有用。

编辑:正如其他人指出的那样,iOS7中的这个解决方案不再有用,因为uniqueIdentifier不再可用,查询mac地址现在总是返回02:00:00:00:00


看看这个,

我们可以使用keychain而不是NSUserDefaults类来存储CFUUIDCreate创建的UUID

这样我们就可以避免在重新安装的情况下进行UUID娱乐活动,对于同一应用程序,即使用户再次卸载和重新安装,也要始终获得相同的UUID

当设备被用户重置时,UUID将被重新创建。

我用sfhfkeychainuils尝试了这个方法,它的工作方式很有魅力。


创建自己的UUID,然后将其存储在钥匙链中。因此,即使您的应用程序被卸载,它也会继续存在。在许多情况下,即使用户在设备之间迁移(例如,完全备份和恢复到另一个设备),它也会持续存在。

实际上,就您而言,它成为一个唯一的用户标识符。(甚至比设备标识符更好)。

例子:

我定义了一个用于创建UUID的自定义方法,如下所示:

1
2
3
4
5
6
7
- (NSString *)createNewUUID
{
    CFUUIDRef theUUID = CFUUIDCreate(NULL);
    CFStringRef string = CFUUIDCreateString(NULL, theUUID);
    CFRelease(theUUID);
    return [(NSString *)string autorelease];
}

然后,您可以在应用程序首次发布时将其存储在KEYCHAIN中。这样,在第一次启动之后,我们就可以简单地从keychain中使用它,不需要重新生成它。使用keychain进行存储的主要原因是:当您将UUID设置为keychain时,即使用户完全卸载应用程序,然后重新安装,它也会持续存在。所以,这是永久性的存储方式,这意味着密钥将一直是唯一的。

1
2
     #import"SSKeychain.h"
     #import <Security/Security.h>

应用程序启动时包括以下代码:

1
2
3
4
5
6
7
8
 // getting the unique key (if present ) from keychain , assuming"your app identifier" as a key
       NSString *retrieveuuid = [SSKeychain passwordForService:@"your app identifier" account:@"user"];
      if (retrieveuuid == nil) { // if this is the first time app lunching , create key for device
        NSString *uuid  = [self createNewUUID];
// save newly created key to Keychain
        [SSKeychain setPassword:uuid forService:@"your app identifier" account:@"user"];
// this is the one time process
}

从sskeychain下载sskeychain.m和.h文件,并将sskeychain.m和.h文件拖到项目中,然后将"security.framework"添加到项目中。要在以后使用UUID,只需使用:

1
NSString *retrieveuuid = [SSKeychain passwordForService:@"your app identifier" account:@"user"];


也许您可以使用:

1
[UIDevice currentDevice].identifierForVendor.UUIDString

苹果的文档描述了供应商的标识,如下所示:

对于来自同一设备上运行的同一供应商的应用程序,此属性的值相同。对于来自不同供应商的同一设备上的应用程序,以及不同设备上的应用程序,无论供应商如何,都会返回不同的值。


您可能会考虑使用OpenUDID,这是对已弃用的UDID的替代。

基本上,为了与UDID匹配,需要以下特性:

  • 唯一或足够唯一(低概率碰撞是指可能非常可以接受)
  • 在重新启动、还原和卸载过程中保持不变
  • 在不同供应商的应用程序之间可用(有助于通过CPI网络获取用户)
  • OpenUDID实现了上述功能,甚至还有一个内置的退出机制供以后考虑。

    检查http://openudid.org,它指向相应的github。希望这有帮助!

    作为旁注,我会避开任何MAC地址选择。虽然mac地址看起来像是一个诱人的通用解决方案,但请确保这个低挂水果是有毒的。MAC地址是非常敏感的,在你说"提交这个应用程序"之前,苹果很可能会拒绝访问这个地址…MAC网络地址用于验证专用LAN(WLAN)或其他虚拟专用网络(VPN)上的某些设备。…它比以前的UDID更敏感!


    可能有帮助:使用下面的代码它将始终是唯一的,除非您擦除(格式化)您的设备。

    1
    2
    UIDevice *myDevice=[UIDevice currentDevice];
    NSString *UUID = [[myDevice identifierForVendor] UUIDString];


    对于iOS6,苹果建议您使用nsuuid类。

    uniqueIdentifier属性的uidevice docs中的消息来看:

    Deprecated in iOS 5.0. Use the identifierForVendor property of this
    class or the advertisingIdentifier property of the ASIdentifierManager
    class instead, as appropriate, or use the UUID method of the NSUUID
    class to create a UUID and write it to the user defaults database.


    我敢肯定苹果公司用这种改变激怒了很多人。我为iOS开发了一个簿记应用程序,并有一个在线服务来同步在不同设备上所做的更改。该服务维护所有设备的数据库以及需要传播给它们的更改。因此,了解哪些设备是哪个非常重要。我在跟踪使用uidevice唯一标识符的设备,为了它的价值,这里是我的想法。

    • 生成UUID并存储在用户默认值中?不好,因为当用户删除应用程序时,这不会持续。如果他们以后再安装,在线服务不应该创建新的设备记录,这将浪费服务器上的资源,并给出包含相同设备的设备列表两次或多次。如果用户重新安装了应用程序,他们会看到多个"Bob的iPhone"被列出。

    • 生成一个UUID并存储在钥匙链中?这是我的计划,因为即使卸载了应用程序,它也会继续存在。但在将iTunes备份恢复到新的iOS设备时,如果备份被加密,则密钥链会被传输。如果新设备和旧设备都在使用中,这可能会导致两个设备包含相同的设备ID。这些设备应作为在线服务中的两个设备列出,即使设备名称相同。

    • 生成一个哈希MAC地址和绑定ID?这看起来是我所需要的最佳解决方案。通过使用bundle id散列,生成的设备id将无法在应用程序之间跟踪设备,并且我将获得应用程序+设备组合的唯一ID。

    有趣的是,苹果自己的文档通过计算系统MAC地址加上捆绑ID和版本的散列值来验证Mac应用商店的收据。所以这似乎是允许的政策,它是否通过应用程序审查,我还不知道。


    我还建议将uniqueIdentifier改为这个开放源代码库(实际上有两个简单的类别),它利用设备的MAC地址和应用程序包标识符在应用程序中生成一个唯一的ID,可以用作UDID替换。

    请记住,与UDID不同,每个应用程序的这个数字都不同。

    您只需导入包含的NSStringUIDevice类别,然后这样调用[[UIDevice currentDevice] uniqueDeviceIdentifier]

    1
    2
    3
    #import"UIDevice+IdentifierAddition.h"
    #import"NSString+MD5Addition.h"
    NSString *iosFiveUDID = [[UIDevice currentDevice] uniqueDeviceIdentifier]

    您可以在Github上找到它:

    iOS 5唯一标识符的uidevice

    以下是类别(仅.m文件-检查github项目的头文件):

    UIDevice+IdentifierAddition.m

    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
    #import"UIDevice+IdentifierAddition.h"
    #import"NSString+MD5Addition.h"

    #include <sys/socket.h> // Per msqr
    #include <sys/sysctl.h>
    #include <net/if.h>
    #include <net/if_dl.h>

    @interface UIDevice(Private)

    - (NSString *) macaddress;

    @end

    @implementation UIDevice (IdentifierAddition)

    ////////////////////////////////////////////////////////////////////////////////
    #pragma mark -
    #pragma mark Private Methods

    // Return the local MAC addy
    // Courtesy of FreeBSD hackers email list
    // Accidentally munged during previous update. Fixed thanks to erica sadun & mlamb.
    - (NSString *) macaddress{
    &nbsp;&nbsp;&nbsp;&nbsp;
    &nbsp;&nbsp;&nbsp;&nbsp;int                 mib[6];
    &nbsp;&nbsp;&nbsp;&nbsp;size_t              len;
    &nbsp;&nbsp;&nbsp;&nbsp;char                *buf;
    &nbsp;&nbsp;&nbsp;&nbsp;unsigned char       *ptr;
    &nbsp;&nbsp;&nbsp;&nbsp;struct if_msghdr    *ifm;
    &nbsp;&nbsp;&nbsp;&nbsp;struct sockaddr_dl  *sdl;
    &nbsp;&nbsp;&nbsp;&nbsp;
    &nbsp;&nbsp;&nbsp;&nbsp;mib[0] = CTL_NET;
    &nbsp;&nbsp;&nbsp;&nbsp;mib[1] = AF_ROUTE;
    &nbsp;&nbsp;&nbsp;&nbsp;mib[2] = 0;
    &nbsp;&nbsp;&nbsp;&nbsp;mib[3] = AF_LINK;
    &nbsp;&nbsp;&nbsp;&nbsp;mib[4] = NET_RT_IFLIST;
    &nbsp;&nbsp;&nbsp;&nbsp;
    &nbsp;&nbsp;&nbsp;&nbsp;if ((mib[5] = if_nametoindex("en0")) == 0) {
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("Error: if_nametoindex error
    ");
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return NULL;
    &nbsp;&nbsp;&nbsp;&nbsp;}
    &nbsp;&nbsp;&nbsp;&nbsp;
    &nbsp;&nbsp;&nbsp;&nbsp;if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("Error: sysctl, take 1
    ");
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return NULL;
    &nbsp;&nbsp;&nbsp;&nbsp;}
    &nbsp;&nbsp;&nbsp;&nbsp;
    &nbsp;&nbsp;&nbsp;&nbsp;if ((buf = malloc(len)) == NULL) {
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("Could not allocate memory. error!
    ");
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return NULL;
    &nbsp;&nbsp;&nbsp;&nbsp;}
    &nbsp;&nbsp;&nbsp;&nbsp;
    &nbsp;&nbsp;&nbsp;&nbsp;if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("Error: sysctl, take 2");
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return NULL;
    &nbsp;&nbsp;&nbsp;&nbsp;}
    &nbsp;&nbsp;&nbsp;&nbsp;
    &nbsp;&nbsp;&nbsp;&nbsp;ifm = (struct if_msghdr *)buf;
    &nbsp;&nbsp;&nbsp;&nbsp;sdl = (struct sockaddr_dl *)(ifm + 1);
    &nbsp;&nbsp;&nbsp;&nbsp;ptr = (unsigned char *)LLADDR(sdl);
    &nbsp;&nbsp;&nbsp;&nbsp;NSString *outstring = [NSString stringWithFormat:@"%02X:%02X:%02X:%02X:%02X:%02X",
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*ptr, *(ptr+1), *(ptr+2), *(ptr+3), *(ptr+4), *(ptr+5)];
    &nbsp;&nbsp;&nbsp;&nbsp;free(buf);
    &nbsp;&nbsp;&nbsp;&nbsp;
    &nbsp;&nbsp;&nbsp;&nbsp;return outstring;
    }

    ////////////////////////////////////////////////////////////////////////////////
    #pragma mark -
    #pragma mark Public Methods

    - (NSString *) uniqueDeviceIdentifier{
    &nbsp;&nbsp;&nbsp;&nbsp;NSString *macaddress = [[UIDevice currentDevice] macaddress];
    &nbsp;&nbsp;&nbsp;&nbsp;NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];&nbsp;&nbsp;
    &nbsp;&nbsp;&nbsp;&nbsp;NSString *stringToHash = [NSString stringWithFormat:@"%@%@",macaddress,bundleIdentifier];
    &nbsp;&nbsp;&nbsp;&nbsp;NSString *uniqueIdentifier = [stringToHash stringFromMD5];&nbsp;&nbsp;
    &nbsp;&nbsp;&nbsp;&nbsp;return uniqueIdentifier;
    }

    - (NSString *) uniqueGlobalDeviceIdentifier{
    &nbsp;&nbsp;&nbsp;&nbsp;NSString *macaddress = [[UIDevice currentDevice] macaddress];
    &nbsp;&nbsp;&nbsp;&nbsp;NSString *uniqueIdentifier = [macaddress stringFromMD5];&nbsp;&nbsp;&nbsp;&nbsp;
    &nbsp;&nbsp;&nbsp;&nbsp;return uniqueIdentifier;
    }

    @end

    NSString+MD5Addition.m:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    #import"NSString+MD5Addition.h"
    #import <CommonCrypto/CommonDigest.h>

    @implementation NSString(MD5Addition)

    - (NSString *) stringFromMD5{
    &nbsp;&nbsp;&nbsp;&nbsp;
    &nbsp;&nbsp;&nbsp;&nbsp;if(self == nil || [self length] == 0)
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return nil;
    &nbsp;&nbsp;&nbsp;&nbsp;
    &nbsp;&nbsp;&nbsp;&nbsp;const char *value = [self UTF8String];
    &nbsp;&nbsp;&nbsp;&nbsp;
    &nbsp;&nbsp;&nbsp;&nbsp;unsigned char outputBuffer[CC_MD5_DIGEST_LENGTH];
    &nbsp;&nbsp;&nbsp;&nbsp;CC_MD5(value, strlen(value), outputBuffer);
    &nbsp;&nbsp;&nbsp;&nbsp;
    &nbsp;&nbsp;&nbsp;&nbsp;NSMutableString *outputString = [[NSMutableString alloc] initWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
    &nbsp;&nbsp;&nbsp;&nbsp;for(NSInteger count = 0; count < CC_MD5_DIGEST_LENGTH; count++){
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[outputString appendFormat:@"%02x",outputBuffer[count]];
    &nbsp;&nbsp;&nbsp;&nbsp;}
    &nbsp;&nbsp;&nbsp;&nbsp;return [outputString autorelease];
    }

    @end


    您可以通过以下代码实现:uidevice-with-uniqueidentifier-for-ios-5


    iOS6中引入的UIDevice identifierForVendor可以满足您的需要。

    identifierForVendor是一个字母数字字符串,它唯一地向应用程序供应商标识设备。(只读)

    1
    @property(nonatomic, readonly, retain) NSUUID *identifierForVendor

    对于来自同一设备上运行的同一供应商的应用程序,此属性的值相同。对于来自不同供应商的同一设备上的应用程序,以及来自不同供应商的不同设备上的应用程序,返回不同的值。

    iOS 6.0及更高版本提供,并在UIDevice.h中声明

    对于iOS 5,请参阅此链接uidevice-with-uniqueidentifier-for-iOS-5


    使用上面提到的sskeychain和代码。下面是要复制/粘贴的代码(添加sskeychain模块):

    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
    +(NSString *) getUUID {

    //Use the bundle name as the App identifier. No need to get the localized version.

    NSString *Appname = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"];    

    //Check if we have UUID already

    NSString *retrieveuuid = [SSKeychain passwordForService:Appname account:@"user"];

    if (retrieveuuid == NULL)
    {

        //Create new key for this app/device

        CFUUIDRef newUniqueId = CFUUIDCreate(kCFAllocatorDefault);

        retrieveuuid = (__bridge_transfer NSString*)CFUUIDCreateString(kCFAllocatorDefault, newUniqueId);

        CFRelease(newUniqueId);

        //Save key to Keychain
        [SSKeychain setPassword:retrieveuuid forService:Appname account:@"user"];
    }

    return retrieveuuid;

    }


    MAC地址可以被欺骗,这使得这种方法对于将内容绑定到特定用户或实现黑名单等安全功能没有用处。

    经过进一步的研究,我觉得到目前为止我们还没有合适的选择。我真的希望苹果会重新考虑他们的决定。

    也许这是一个好主意,通过电子邮件向苹果发送关于这个主题和/或提交一个bug/功能请求,因为他们可能甚至不知道开发人员的全部后果。


    以下代码有助于获取UDID:

    1
    2
            udid = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
            NSLog(@"UDID : %@", udid);

    这是我用来获取iOS 5和iOS 6、7的ID的代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    - (NSString *) advertisingIdentifier
    {
        if (!NSClassFromString(@"ASIdentifierManager")) {
            SEL selector = NSSelectorFromString(@"uniqueIdentifier");
            if ([[UIDevice currentDevice] respondsToSelector:selector]) {
                return [[UIDevice currentDevice] performSelector:selector];
            }
        }
        return [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
    }


    从iOS 6开始,我们有符合RFC4122的NSUUID

    Apple Link:Apple参考


    iOS 11引入了devicecheck框架。它有一个完整的解决方案来唯一地识别设备。


    你可以使用

    1
    NSString *sID = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];

    它在所有应用程序中对设备都是唯一的。


    苹果在iOS11中添加了一个名为devicecheck的新框架,它将帮助您非常容易地获得唯一的标识符。阅读此表单了解更多信息。https://medium.com/@santoshbotre01/unique-identifier-for-the-ios-devices-590bb778290d


    如果有人在寻找替代方案时偶然发现这个问题。我在IDManager课上就采用了这种方法,这是来自不同解决方案的集合。keychainutil是要从keychain中读取的包装器。您还可以使用hashed MAC address作为一种唯一的ID。

    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
    /*  Apple confirmed this bug in their system in response to a Technical Support Incident&nbsp;
    &nbsp; &nbsp; request. They said that identifierForVendor and advertisingIdentifier sometimes&nbsp;
    &nbsp; &nbsp; returning all zeros can be seen both in development builds and apps downloaded over the&nbsp;
    &nbsp; &nbsp; air from the App Store. They have no work around and can't say when the problem will be fixed. */
    #define kBuggyASIID             @"00000000-0000-0000-0000-000000000000"

    + (NSString *) getUniqueID {
        if (NSClassFromString(@"ASIdentifierManager")) {
            NSString * asiID = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
            if ([asiID compare:kBuggyASIID] == NSOrderedSame) {
                NSLog(@"Error: This device return buggy advertisingIdentifier.");
                return [IDManager getUniqueUUID];
            } else {
                return asiID;
            }

        } else {
            return [IDManager getUniqueUUID];
        }
    }


    + (NSString *) getUniqueUUID {
        NSError * error;
        NSString * uuid = [KeychainUtils getPasswordForUsername:kBuyassUser andServiceName:kIdOgBetilngService error:&error];
        if (error) {
            NSLog(@"Error geting unique UUID for this device! %@", [error localizedDescription]);
            return nil;
        }
        if (!uuid) {
            DLog(@"No UUID found. Creating a new one.");
            uuid = [IDManager GetUUID];
            uuid = [Util md5String:uuid];
            [KeychainUtils storeUsername:USER_NAME andPassword:uuid forServiceName:SERVICE_NAME updateExisting:YES error:&error];
            if (error) {
                NSLog(@"Error getting unique UUID for this device! %@", [error localizedDescription]);
                return nil;
            }
        }
        return uuid;
    }

    /* NSUUID is after iOS 6. */
    + (NSString *)GetUUID
    {
        CFUUIDRef theUUID = CFUUIDCreate(NULL);
        CFStringRef string = CFUUIDCreateString(NULL, theUUID);
        CFRelease(theUUID);
        return [(NSString *)string autorelease];
    }

    #pragma mark - MAC address
    // Return the local MAC addy
    // Courtesy of FreeBSD hackers email list
    // Last fallback for unique identifier
    + (NSString *) getMACAddress
    {
        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
    ");
            return NULL;
        }

        if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
            printf("Error: sysctl, take 1
    ");
            return NULL;
        }

        if ((buf = malloc(len)) == NULL) {
            printf("Error: Memory allocation error
    ");
            return NULL;
        }

        if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
            printf("Error: sysctl, take 2
    ");
            free(buf); // Thanks, Remy"Psy" Demerest
            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)];

        free(buf);
        return outstring;
    }

    + (NSString *) getHashedMACAddress
    {
        NSString * mac = [IDManager getMACAddress];
        return [Util md5String:mac];
    }

    + (NSString *)md5String:(NSString *)plainText
    {
        if(plainText == nil || [plainText length] == 0)
            return nil;

        const char *value = [plainText UTF8String];
        unsigned char outputBuffer[CC_MD5_DIGEST_LENGTH];
        CC_MD5(value, strlen(value), outputBuffer);

        NSMutableString *outputString = [[NSMutableString alloc] initWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
        for(NSInteger count = 0; count < CC_MD5_DIGEST_LENGTH; count++){
            [outputString appendFormat:@"%02x",outputBuffer[count]];
        }
        NSString * retString = [NSString stringWithString:outputString];
        [outputString release];
        return retString;
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    + (NSString *) getUniqueUUID {
        NSError * error;
        NSString * uuid = [KeychainUtils getPasswordForUsername:kBuyassUser andServiceName:kIdOgBetilngService error:&error];
        if (error) {
        NSLog(@"Error geting unique UUID for this device! %@", [error localizedDescription]);
        return nil;
        }
        if (!uuid) {
            DLog(@"No UUID found. Creating a new one.");
            uuid = [IDManager GetUUID];
            uuid = [Util md5String:uuid];
            [KeychainUtils storeUsername:USER_NAME andPassword:uuid forServiceName:SERVICE_NAME updateExisting:YES error:&error];
            if (error) {
                NSLog(@"Error getting unique UUID for this device! %@", [error localizedDescription]);
                return nil;
            }
        }
        return uuid;
    }

    我们可以为iOS7的供应商使用标识符,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    -(NSString*)uniqueIDForDevice
    {
        NSString* uniqueIdentifier = nil;
        if( [UIDevice instancesRespondToSelector:@selector(identifierForVendor)] ) { // >=iOS 7
            uniqueIdentifier = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
        } else { //<=iOS6, Use UDID of Device      
                CFUUIDRef uuid = CFUUIDCreate(NULL);
                //uniqueIdentifier = ( NSString*)CFUUIDCreateString(NULL, uuid);- for non- ARC
                uniqueIdentifier = ( NSString*)CFBridgingRelease(CFUUIDCreateString(NULL, uuid));// for ARC
                CFRelease(uuid);
             }
        }
    return uniqueIdentifier;
    }

    --重要提示---

    供应商的udid和identifier不同:---

    1
    2
    3
    4
    5
    1.) On uninstalling  and reinstalling the app identifierForVendor will change.

    2.) The value of identifierForVendor remains the same for all the apps installed from the same vendor on the device.

    3.) The value of identifierForVendor also changes for all the apps if any of the app (from same vendor) is reinstalled.


    从iOS7开始,苹果已经从所有公共API中隐藏了UDID。任何以ffff开头的udid都是假的id。以前工作过的"发送udid"应用程序不能再用于为测试设备收集udid。(叹气!)

    当设备连接到xcode时(在管理器中),当设备连接到iTunes时(尽管您必须单击"序列号"才能显示标识符),会显示udid。

    如果您需要获取设备的udid以添加到配置文件中,但无法在xcode中自己完成,则必须引导他们完成从iTunes复制/粘贴该设备的步骤。

    自iOS 7发布以来,有没有一种方法可以在PC/Mac上不使用iTunes的情况下获得udid?


    我也遇到了一些问题,解决方法很简单:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
        // Get Bundle Info for Remote Registration (handy if you have more than one app)
        NSString *appName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleDisplayName"];
        NSString *appVersion = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"];


        // Get the users Device Model, Display Name, Unique ID, Token & Version Number
        UIDevice *dev = [UIDevice currentDevice];
        NSString *deviceUuid=[dev.identifierForVendor  UUIDString];

        NSString *deviceName = dev.name;

    一个不完美的,但最接近于udid的替代品之一(在使用iOS 8.1和xcode 6.1的swift中):

    生成随机UUID

    1
    let strUUID: String = NSUUID().UUIDString

    并使用keychainwrapper库:

    向keychain添加字符串值:

    1
    let saveSuccessful: Bool = KeychainWrapper.setString("Some String", forKey:"myKey")

    从keychain检索字符串值:

    1
    let retrievedString: String? = KeychainWrapper.stringForKey("myKey")

    从keychain中移除字符串值:

    1
    let removeSuccessful: Bool = KeychainWrapper.removeObjectForKey("myKey")

    此解决方案使用keychain,因此存储在keychain中的记录将被保留,即使在卸载并重新安装应用程序之后也是如此。删除此记录的唯一方法是重置设备的所有内容和设置。这就是为什么我提到这个替换解决方案并不完美,但它仍然是iOS 8.1上使用swift替换UDID的最佳解决方案之一。


    获得UDID的一种工作方式:

  • 在应用程序内部启动一个Web服务器,它有两个页面:一个页面返回精心设计的MobileConfiguration配置文件,另一个页面收集UDID。这里,这里和这里有更多的信息。
  • 从应用程序内部打开MobileSafari中的第一页,它会将您重定向到settings.app,要求安装配置文件。安装完配置文件后,udid将被发送到第二个网页,您可以从应用程序内部访问它。(settings.app具有所有必要的权限和不同的沙盒规则)。
  • 使用RoutingHTTPServer的示例:

    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
    import UIKit
    import RoutingHTTPServer

    @UIApplicationMain
    class AppDelegate: UIResponder, UIApplicationDelegate {
        var bgTask = UIBackgroundTaskInvalid
        let server = HTTPServer()

        func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
            application.openURL(NSURL(string:"http://localhost:55555")!)
            return true
        }

        func applicationDidEnterBackground(application: UIApplication) {
            bgTask = application.beginBackgroundTaskWithExpirationHandler() {
                dispatch_async(dispatch_get_main_queue()) {[unowned self] in
                    application.endBackgroundTask(self.bgTask)
                    self.bgTask = UIBackgroundTaskInvalid
                }
            }
        }
    }

    class HTTPServer: RoutingHTTPServer {
        override init() {
            super.init()
            setPort(55555)
            handleMethod("GET", withPath:"/") {
                $1.setHeader("Content-Type", value:"application/x-apple-aspen-config")
                $1.respondWithData(NSData(contentsOfFile: NSBundle.mainBundle().pathForResource("udid", ofType:"mobileconfig")!)!)
            }
            handleMethod("POST", withPath:"/") {
                let raw = NSString(data:$0.body(), encoding:NSISOLatin1StringEncoding) as! String
                let plistString = raw.substringWithRange(Range(start: raw.rangeOfString("<?xml")!.startIndex,end: raw.rangeOfString("</plist>")!.endIndex))
                let plist = NSPropertyListSerialization.propertyListWithData(plistString.dataUsingEncoding(NSISOLatin1StringEncoding)!, options: .allZeros, format: nil, error: nil) as! [String:String]

                let udid = plist["UDID"]!
                println(udid) // Here is your UDID!

                $1.statusCode = 200
                $1.respondWithString("see https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/iPhoneOTAConfiguration/ConfigurationProfileExamples/ConfigurationProfileExamples.html")
            }
            start(nil)
        }
    }

    以下是udid.mobileconfig的内容:

    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
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC"-//Apple//DTD PLIST 1.0//EN""http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
        <dict>
            <key>PayloadContent</key>
            <dict>
                <key>URL</key>
                <string>http://localhost:55555</string>
                <key>DeviceAttributes</key>
               
                    <string>IMEI</string>
                    <string>UDID</string>
                    <string>PRODUCT</string>
                    <string>VERSION</string>
                    <string>SERIAL</string>
                </array>
            </dict>
            <key>PayloadOrganization</key>
            <string>udid</string>
            <key>PayloadDisplayName</key>
            <string>Get Your UDID</string>
            <key>PayloadVersion</key>
            <integer>1</integer>
            <key>PayloadUUID</key>
            <string>9CF421B3-9853-9999-BC8A-982CBD3C907C</string>
            <key>PayloadIdentifier</key>
            <string>udid</string>
            <key>PayloadDescription</key>
            <string>Install this temporary profile to find and display your current device's UDID. It is automatically removed from device right after you get your UDID.</string>
            <key>PayloadType</key>
            <string>Profile Service</string>
        </dict>
    </plist>

    配置文件安装将失败(我没有费心实现预期的响应,请参阅文档),但应用程序将获得正确的UDID。你还应该在MobileConfig上签名。


    对于Swift 3.0,请使用以下代码。

    1
    2
    let deviceIdentifier: String = (UIDevice.current.identifierForVendor?.uuidString)!
    NSLog("output is : %@", deviceIdentifier)


    nslog(@"%@",[[uidevice currentdevice]identifierforvendor]);


    不要使用这些库-libomnitureAppMeasurement,它使用的是苹果不再支持的唯一标识符。


    小黑客为你:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    /**
     @method uniqueDeviceIdentifier
     @abstract A unique device identifier is a hash value composed from various hardware identifiers such
     as the device’s serial number. It is guaranteed to be unique for every device but cannot
     be tied to a user account. [UIDevice Class Reference]
     @return An 1-way hashed identifier unique to this device.
     */
    + (NSString *)uniqueDeviceIdentifier {      
        NSString *systemId = nil;
        // We collect it as long as it is available along with a randomly generated ID.
        // This way, when this becomes unavailable we can map existing users so the
        // new vs returning counts do not break.
        if (([[[UIDevice currentDevice] systemVersion] floatValue] < 6.0f)) {
            SEL udidSelector = NSSelectorFromString(@"uniqueIdentifier");
            if ([[UIDevice currentDevice] respondsToSelector:udidSelector]) {
                systemId = [[UIDevice currentDevice] performSelector:udidSelector];
            }
        }
        else {
            systemId = [NSUUID UUID];
        }
        return systemId;
    }