Keychain以及NSUserdefault清空方法

開(kāi)發(fā)過(guò)程中,我們都會(huì)使用Keychain或者UserDefaults來(lái)存儲(chǔ)一些App登錄用戶相關(guān)的數(shù)據(jù),或者服務(wù)器的token等,可以很方便的實(shí)現(xiàn)App啟動(dòng)自登錄的功能;但是這個(gè)對(duì)于測(cè)試來(lái)說(shuō)就比較頭疼,尤其是存儲(chǔ)到Keychain里的不能刪除的數(shù)據(jù),而要模仿一個(gè)全新的從沒(méi)有安裝過(guò)我們應(yīng)用的App的時(shí)候,必須要清除掉保存的Keychain。

1. Keychain清除

Keychain是iOS設(shè)備中的一個(gè)安全的存儲(chǔ)容器,可以用來(lái)為應(yīng)用保存敏感信息比如用戶名,密碼,網(wǎng)絡(luò)密碼,認(rèn)證令牌。在刪掉應(yīng)用后,Keychain中的數(shù)據(jù)也會(huì)保留,用戶再次安裝的App還能從Keychain中讀取到數(shù)據(jù)。所以Keychain中保存的數(shù)據(jù)一般都是比較重要,并且不希望卸載應(yīng)用后就被刪除的數(shù)據(jù),比如用戶的登錄信息,wifi密碼等。我們常用的OpenUDID庫(kù)就是使用Keychain的這個(gè)特性來(lái)獲取唯一性的設(shè)備ID,具體做法是,生成一個(gè)UUID,或者是獲取廣告標(biāo)識(shí)符,然后存入Keychain,這樣刪除應(yīng)用后再次安裝應(yīng)用也還能讀取到上一次生成的設(shè)備標(biāo)識(shí)符。

添加操作
+ (void)save:(NSString*)service data:(NSData*)data
{
//    OSStatus ret = 100;
    NSMutableDictionary * keyChainDic = [NSMutableDictionary dictionaryWithObjectsAndKeys:
                                         (__bridge_transfer id)kSecClassGenericPassword, (__bridge_transfer id)kSecClass,
                                         service, (__bridge_transfer id)kSecAttrService,
                                         service, (__bridge_transfer id)kSecAttrAccount, nil];
    if (noErr != SecItemDelete((__bridge_retained CFDictionaryRef)keyChainDic))
    {
        DDMLogError(KEY_CHAIN, @"save : SecItemDelete failed!");
    }
    
    [keyChainDic setObject:[NSKeyedArchiver archivedDataWithRootObject:data]
                    forKey:(__bridge_transfer id)kSecValueData];
    
    if (noErr != SecItemAdd((__bridge_retained CFDictionaryRef)keyChainDic, NULL))
    {
        DDMLogError(KEY_CHAIN, @"save : SecItemAdd failed!");
    }
}
指定讀取
+ (NSData*)load:(NSString*)service
{
    NSData * ret = nil;
    NSMutableDictionary * keyChainDic = [NSMutableDictionary dictionaryWithObjectsAndKeys:
                                         (__bridge_transfer id)kSecClassGenericPassword, (__bridge_transfer id)kSecClass,
                                         service, (__bridge_transfer id)kSecAttrService,
                                         service, (__bridge_transfer id)kSecAttrAccount, nil];
    
    [keyChainDic setObject:(__bridge_transfer id)kCFBooleanTrue
                    forKey:(__bridge_transfer id)kSecReturnData];
    [keyChainDic setObject:(__bridge_transfer id)kSecMatchLimitOne
                    forKey:(__bridge_transfer id)kSecMatchLimit];
    
    CFDataRef keyData = NULL;
    
    if (noErr == SecItemCopyMatching((__bridge_retained CFDictionaryRef)keyChainDic,
                            (CFTypeRef*)&keyData))
    {
        @try
        {
            ret = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge_transfer NSData *)keyData];
        }
        @catch (NSException *e)
        {
            NSLog(@"Unarchive of %@ failed: %@", service, e);
        }
        @finally
        {
        }
    }
    
    return ret;
}

清空Keychain的思路很簡(jiǎn)單,就是遍歷keychain中存儲(chǔ)的數(shù)據(jù),然后挨個(gè)刪除。代碼如下:

1.全部刪除
- (void)clearKeyChain {
    NSMutableDictionary *query = [NSMutableDictionary dictionaryWithObjectsAndKeys:
                                  (__bridge id)kCFBooleanTrue, (__bridge id)kSecReturnAttributes,
                                  (__bridge id)kSecMatchLimitAll, (__bridge id)kSecMatchLimit,
                                  nil];
    NSArray *secItemClasses = [NSArray arrayWithObjects:
                               (__bridge id)kSecClassGenericPassword,
                               (__bridge id)kSecClassInternetPassword,
                               (__bridge id)kSecClassCertificate,
                               (__bridge id)kSecClassKey,
                               (__bridge id)kSecClassIdentity,
                               nil];
    for (id secItemClass in secItemClasses) {
        [query setObject:secItemClass forKey:(__bridge id)kSecClass];
         
        CFTypeRef result = NULL;
        SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
        if (result != NULL) CFRelease(result);
         
        NSDictionary *spec = @{(__bridge id)kSecClass: secItemClass};
        SecItemDelete((__bridge CFDictionaryRef)spec);
    }
}
2.指定刪除
+ (void)delete:(NSString *)service {
    NSMutableDictionary * keyChainDic = [NSMutableDictionary dictionaryWithObjectsAndKeys:
                                         (__bridge_transfer id)kSecClassGenericPassword, (__bridge_transfer id)kSecClass,
                                         service, (__bridge_transfer id)kSecAttrService,
                                         service, (__bridge_transfer id)kSecAttrAccount, nil];
    if (noErr != SecItemDelete((__bridge_retained CFDictionaryRef)keyChainDic))
    {
        DDMLogError(KEY_CHAIN, @"delete : SecItemDelete failed!");
    }
}

2. NSUserDefault的清除

UserDefaults是iOS設(shè)備上一個(gè)Key-Value的存儲(chǔ)容器,最終數(shù)據(jù)會(huì)以plist文件的形式存在文件夾Library/Preferences下面。默認(rèn)的UserDefaults的文件名是bundle identitier加上.plist的后綴名。UserDefaulst會(huì)隨App的刪除而被清除(也可以同步到iCloud上),所以UserDefaults存儲(chǔ)著一些可以再生的的數(shù)據(jù)。

- (void)clearUserDefaults {
// 清空默認(rèn)存儲(chǔ)
    NSString *appDomain = [[NSBundle mainBundle] bundleIdentifier];
    [[NSUserDefaults standardUserDefaults] removePersistentDomainForName:appDomain];
    [[NSUserDefaults standardUserDefaults] synchronize];
 // 清空自定義存儲(chǔ)
    NSString *path = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES).lastObject;
    path = [path stringByAppendingPathComponent:@"Preferences"];
    NSArray *fileList = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:nil];
     
    for (NSString * filename in fileList) {
        NSString *filepath = [path stringByAppendingPathComponent:filename];
        BOOL isDir = NO;
        [[NSFileManager defaultManager] fileExistsAtPath:filepath isDirectory:(&isDir)];
        if (!isDir && [filename hasSuffix:@".plist"] && (![filename isEqualToString:appDomain])) {
            NSString *suitename = [filename stringByDeletingPathExtension];
            NSUserDefaults *userDefaults = [[NSUserDefaults alloc] initWithSuiteName:suitename];
            [userDefaults removePersistentDomainForName:suiteName];
            [[NSFileManager defaultManager] removeItemAtPath:filepath error:nil];
        }
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容