刚刚发现,UIDevice uniqueIdentifier属性在iOS 5中已弃用,在iOS 7及以上版本中不可用。似乎没有可供选择的方法或属性。
我们现有的许多应用程序都紧密依赖于这个属性来唯一地识别特定的设备。今后我们该如何处理这个问题?
2011-2012年的文件建议:
特殊注意事项
不要使用uniqueIdentifier属性。创建特定的唯一标识符
你可以调用CFUUIDCreate函数来创建一个UUID,然后写入
使用NSUserDefaults类将它转换到默认数据库。
但是,如果用户卸载和重新安装应用程序,这个值就不一样了。
如果有人在寻找替代方案时偶然发现了这个问题。我在IDManager课上就采用了这种方法,
这是不同解的集合。KeyChainUtil是一个从keychain读取的包装器。
您还可以使用散列MAC地址作为一种唯一ID。
/* Apple confirmed this bug in their system in response to a Technical Support Incident
request. They said that identifierForVendor and advertisingIdentifier sometimes
returning all zeros can be seen both in development builds and apps downloaded over the
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\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("Error: Memory allocation error\n");
return NULL;
}
if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
printf("Error: sysctl, take 2\n");
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;
}
根据@moonlight提出的链接,我做了几次测试,这似乎是最好的解决方案。正如@DarkDust所说,该方法将检查始终可用的en0。
有两种选择:
uniqueDeviceIdentifier (MAC+CFBundleIdentifier的MD5)
和uniqueGlobalDeviceIdentifier(MAC的MD5),这些总是返回相同的值。
下面是我所做的测试(用真正的设备):
#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
希望对大家有用。
编辑:
正如其他人指出的那样,这个解决方案在iOS 7中不再有用,因为uniqueIdentifier不再可用,查询MAC地址现在总是返回02:00:00:00:00:00
如果用户卸载和重新安装应用程序,CFUUIDCreate创建的UUID是唯一的:你每次都会得到一个新的UUID。
但你可能希望它不是唯一的,即它应该保持不变,当用户卸载和重新安装应用程序。这需要一些努力,因为最可靠的每个设备标识符似乎是MAC地址。您可以查询MAC并使用它作为UUID。
编辑:当然,总是需要查询同一接口的MAC。我想最好的选择是en0。MAC一直存在,即使接口没有IP/ down。
编辑2:正如其他人指出的那样,自iOS 6以来首选的解决方案是-[UIDevice identifierForVendor]。在大多数情况下,你应该可以使用它来替换旧的-[UIDevice uniqueIdentifier](但是当应用程序第一次启动时创建的UUID似乎是苹果想要你使用的)。
编辑3:所以这个重点不会在评论噪音中丢失:不要使用MAC作为UUID,使用MAC创建哈希。哈希每次都会创建相同的结果,即使是在重新安装和应用程序之间(如果哈希以相同的方式完成)。不管怎样,现在(2013年)这已经没有必要了,除非你在iOS < 6.0上需要一个“稳定的”设备标识符。
编辑4:在iOS 7中,苹果现在在查询MAC时总是返回一个固定值,以专门阻止MAC作为ID方案的基础。所以你现在应该使用-[UIDevice identifierForVendor]或者创建一个每个安装的UUID。
iOS 6中引入的UIDevice identifierForVendor可以满足你的需求。
identifierForVendor是一个字母数字字符串,用于唯一标识应用程序供应商的设备。(只读)
@property(nonatomic, readonly, retain) NSUUID *identifierForVendor
对于运行在同一设备上的来自同一供应商的应用程序,此属性的值是相同的。对于同一设备上来自不同供应商的应用程序返回不同的值,对于不同设备上的应用程序返回不同的值,与供应商无关。
在iOS 6.0及更高版本中可用,在UIDevice.h中声明
对于iOS 5,请参考此链接UIDevice-with-UniqueIdentifier-for-iOS-5
我们可以在ios7中使用identifierForVendor,
-(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和identifierForVendor是不同的:——
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.