[UIDevice currentDevice].identifierForVendor.UUIDString
替代。可是這個不是真正的UDID.關閉的緣由是由於隱私問題。以後蘋果禁止上架試圖獲取UDID的應用。##UUIDhtml
0DEF9507-EB5A-471A-8BC7-638A0B0A327D
。可是UUID並不像UDID同樣是唯一的,它只是在某一時空是惟一的,當每次寫在應用以後獲取到的UUID都是不同的。好比經過一個for循環打印一下UUID能就能看出不同:for (int i = 0; i < 5; i ++) {
NSLog(@"uuid %zd = %@", i,[NSUUID UUID].UUIDString);
}
複製代碼
NSString * uuid = [NSUUID UUID].UUIDString;
複製代碼
蘋果在OS X和IOS系統都有提供的一種安全存儲敏感信息的工具,即keychain。所謂銘感信息,即用戶ID、password、certificate等。keychain裏面存儲的數據是item。這些item是以key-value的形式存儲的,能夠理解爲Dictonary。利用keychain存儲這些信息能夠提升用戶體驗,免除用戶重複輸入用戶名和密碼等繁瑣的操做。同時,蘋果的這套keychain Service安全機制可以保障存儲的信息不會被竊取,因此能夠用來存儲UUID等。git
keychain中是存放的item。而且能夠存聽任意數量的item。keychain會對須要加密的item進行加密保護,好比:密碼。而對於像證書就就不會加密。github
在蘋果提供的API中能夠看到有五種類型的item:安全
kSecClassInternetPassword //Specifies Internet password items.
kSecClassGenericPassword //Specifies generic password items.
kSecClassCertificate //Specifies certificate items.
kSecClassKey //Specifies key items.
kSecClassIdentity //Specifies identity items.
複製代碼
蘋果提供了四種操做item的方法,即增、刪、改、查操做:bash
// 1. 查詢已存在的item/items
SecItemCopyMatching(CFDictionaryRef query, CFTypeRef * __nullable CF_RETURNS_RETAINED result)
// 2. 添加 item/items到keychain
SecItemAdd(CFDictionaryRef attributes, CFTypeRef * __nullable CF_RETURNS_RETAINED result)
// 3. 更新已存在的item/items
SecItemUpdate(CFDictionaryRef query, CFDictionaryRef attributesToUpdate)
// 4. 刪除已存在的 item/items
SecItemDelete(CFDictionaryRef query)
複製代碼
能夠寫一個KeychainWrapper
工具類來實現keychain的操做。核心代碼以下網絡
// 根據特定的Service建立一個用於操做KeyChain的Dictionary
+ (NSMutableDictionary *)getKeychainQuery:(NSString *)service
{
// 添加的字典不懂?
return [NSMutableDictionary dictionaryWithObjectsAndKeys:
(__bridge id)(kSecClassGenericPassword), kSecClass,
service, kSecAttrService,
service, kSecAttrAccount,
kSecAttrAccessibleAfterFirstUnlock, kSecAttrAccessible,
nil];
}
// 保存數據到keychain中
+ (BOOL)saveDate:(id)date withService:(NSString *)service
{
// 1. 建立dictonary
NSMutableDictionary * keychainQuery = [self getKeychainQuery:service];
// 2. 先刪除
SecItemDelete((CFDictionaryRef)keychainQuery);
// 3. 添加到date到query中
[keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:date] forKey:(id<NSCopying>)kSecValueData];
// 4. 存儲到到keychain中
OSStatus status = SecItemAdd((CFDictionaryRef)keychainQuery, NULL);
return status == noErr ? YES : NO;
}
// 從keychain中查找數據
+ (id)searchDateWithService:(NSString *)service
{
id retsult = nil;
NSMutableDictionary * keychainQuery = [self getKeychainQuery:service];
[keychainQuery setObject:(id)kCFBooleanTrue forKey:(id<NSCopying>)kSecReturnData];
[keychainQuery setObject:(id)kSecMatchLimitOne forKey:(id<NSCopying>)kSecMatchLimit];
CFTypeRef resultDate = NULL;
if (SecItemCopyMatching((CFDictionaryRef)keychainQuery, &resultDate)== noErr) {
@try{
retsult = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge NSData *)resultDate];
}
@catch(NSException *e){
NSLog(@"查找數據不存在");
}
@finally{
}
}
if (resultDate) {
CFRelease(resultDate);
}
return retsult;
}
// 更新keychain中的數據
+ (BOOL)updateDate:(id)date withService:(NSString *)service
{
NSMutableDictionary * searchDictonary = [self getKeychainQuery:service];
if (!searchDictonary) {return NO;}
NSMutableDictionary * updateDictonary = [NSMutableDictionary dictionary];
[updateDictonary setObject:[NSKeyedArchiver archivedDataWithRootObject:date] forKey:(id<NSCopying>)kSecValueData];
OSStatus status = SecItemUpdate((CFDictionaryRef)searchDictonary, (CFDictionaryRef)updateDictonary);
return status == noErr ? YES : NO;
}
// 刪除keychain中的數據
+ (BOOL)deleteDateiWithService:(NSString *)service
{
NSMutableDictionary * keychainQuery = [self getKeychainQuery:service];
OSStatus status = SecItemDelete((CFDictionaryRef)keychainQuery);
return status == noErr ? YES : NO;
}
複製代碼
有了上面的方法,接下來就操做就很簡單了:app
/**
先從keychain裏面加載uuid 若是沒有 就獲取uuid並加載到keychain中
*/
+ (NSString *)getUUIDfromKeychain
{
NSString * uuid = NULL;
uuid = [KeychainWrapper searchDateWithService:DEMO_UUID];
if (uuid) {
return uuid;
}else{
uuid = [self getRandomUUID];
if([KeychainWrapper saveDate:uuid withService:DEMO_UUID]){
return uuid;
}else{
return NULL;
}
}
}
+ (NSString *)getRandomUUID
{
return [NSUUID UUID].UUIDString;
}
複製代碼
打印出來發現獲取的uuid是同樣的,說明keychain保存成功了: 框架
/**
* 獲取IDFA,若是用戶關閉此功能,就會存在娶不到的狀況
*/
+ (NSString *)getIDFA
{
return [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
}
複製代碼
com.vender.app1
和com.vender.app2
這兩個BundleID都是屬於同一個供應商,那麼這兩個應用的IDFV都是相同的。原理是經過BundleID的反轉的前兩部分進行匹配,若是相同就是同一個Vender,共享同一個idfv的值。值得一提的是,IDFV是必定能取到的。可是若是用戶將屬於同一個Vender的全部App卸載,則IDFV的值會被重置,當再重裝此Vender的App時IDFV的值和以前不一樣。/**
* 獲取IDFV
*/
+ (NSString *)getIDFV
{
return [[[UIDevice currentDevice] identifierForVendor] UUIDString];
}
複製代碼
獲取運營商很簡單,只須要用到CTTelephonyNetworkInfo
和CTCarrier
兩個類便可,值得注意的是,須要導入兩個頭文件:dom
#import <CoreTelephony/CTTelephonyNetworkInfo.h>
#import <CoreTelephony/CTCarrier.h>
複製代碼
代碼:ide
/**
* 獲取設備運營商
*/
+ (NSString *)getCarrier
{
CTTelephonyNetworkInfo * info = [[CTTelephonyNetworkInfo alloc]init];
CTCarrier * carrier = [info subscriberCellularProvider];
NSString * mobile;
if (!carrier.isoCountryCode) {
NSLog(@"沒有SIM卡");
mobile = @"無運營商";
}else{
mobile = [carrier carrierName];
}
return mobile;
}
複製代碼
判斷網絡類型的方式有幾種:
AFNetworking
判斷 缺點:必須導入該框架。NotReachable // 無網絡鏈接
ReachableViaWiFi // WIFI
ReachableViaWWAN // 蜂窩移動類型
複製代碼
因此還須要經過CTTelephonyNetworkInfo
對蜂窩移動網絡類型判斷。CTTelephonyNetworkInfo中蜂窩移動網絡類型有:
CTRadioAccessTechnologyGPRS
CTRadioAccessTechnologyEdge
CTRadioAccessTechnologyWCDMA
CTRadioAccessTechnologyHSDPA
CTRadioAccessTechnologyHSUPA
CTRadioAccessTechnologyCDMA1x
CTRadioAccessTechnologyCDMAEVDORev0
CTRadioAccessTechnologyCDMAEVDORevA
CTRadioAccessTechnologyCDMAEVDORevB
CTRadioAccessTechnologyeHRPD
CTRadioAccessTechnologyLTE
複製代碼
完整代碼:
/**
* 判斷當前網絡類型
*/
+ (NSString *)getNetworkType
{
Reachability * reachability = [Reachability reachabilityWithHostName:@"www.baidu.com"];
NetworkStatus netStatus = [reachability currentReachabilityStatus];
NSString * networkType = @"";
switch (netStatus) {
case ReachableViaWiFi:
networkType = @"WIFI";
break;
case ReachableViaWWAN:
{
// 判斷蜂窩移動類型
CTTelephonyNetworkInfo * networkInfo = [[CTTelephonyNetworkInfo alloc]init];
if ([networkInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyGPRS]) {
networkType = @"2G";
} else if ([networkInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyEdge]) {
networkType = @"2G";
} else if ([networkInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyWCDMA]) {
networkType = @"3G";
} else if ([networkInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyHSDPA]) {
networkType = @"3G";
} else if ([networkInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyHSUPA]) {
networkType = @"3G";
} else if ([networkInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyCDMA1x]) {
networkType = @"3G";
} else if ([networkInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyCDMAEVDORev0]) {
networkType = @"3G";
} else if ([networkInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyCDMAEVDORevA]) {
networkType = @"3G";
} else if ([networkInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyCDMAEVDORevB]) {
networkType = @"3G";
} else if ([networkInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyeHRPD]) {
networkType = @"3G";
} else if ([networkInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyLTE]) {
networkType = @"4G";
}
}
break;
case NotReachable:
networkType = @"當前無網絡鏈接";
break;
}
return networkType;
}
複製代碼
我把以上代碼都封裝到了DeviceInfo中,須要的能夠直接拖入這個文件便可使用。github連接:DeviceInfo
參考博客: