iOS 數(shù)據(jù)持久化的幾種方法

簡介

持久化就是將數(shù)據(jù)保存到硬盤中,讓APP重啟后可以使用之前保存的數(shù)據(jù).在iOS開發(fā)中,可能會用到一下幾種

  1. plist文件:屬性列表
  2. preference:偏好設(shè)置
  3. NSKeyedArchiver:歸檔
  4. keychain:鑰匙串

沙盒

在介紹存儲方法之前,先說下沙盒機制.iOS程序默認(rèn)情況下只能訪問程序的目錄,這個目錄就是沙盒。
沙盒的目錄結(jié)構(gòu)如下:

  • 應(yīng)用程序包:存放的是應(yīng)用程序的源文件:資源文件和可執(zhí)行文件
NSString *path = [[NSBundle mainBundle] bundlePath];
  • Documents:比較常用的目錄,itune同步會同步這個文件夾的內(nèi)容需曾,適合存儲重要的數(shù)據(jù)
NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
  • Library:
    1.Caches:tunes不會同步此文件夾竿滨,設(shè)個存儲體積大,不需要備份的重要數(shù)據(jù)
NSString *path  = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;
  • tmpitunes不會同步迹鹅,系統(tǒng)可能在應(yīng)用沒運行時就刪除該目錄的文件,適合存臨時文件,用完就刪除:
NSString *path  = NSTemporaryDirectory();

一巫财、plist文件(序列化)

plist文件是通過XML文件的方式保存在目錄中
以下類型可以被序列化:

NSString;//字符串
NSMutableString;//可變字符串
NSArray;//數(shù)組
NSMutableArray;//可變數(shù)組
NSDictionary;//字典
NSMutableDictionary;//可變字典
NSData;//二進制數(shù)據(jù)
NSMutableData;//可變二進制數(shù)據(jù)
NSNumber;//基本數(shù)據(jù)
NSDate;//日期

這里我們就用NSDictionary當(dāng)例子,其他的類型和這個方法類似;

/**
 寫入數(shù)據(jù)
 */
-(void)writeToPlist:(NSDictionary *)dict plistName:(NSString *)plistName{
    //存取路徑
    NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
    //路徑中文件名
    NSString *filePath = [path stringByAppendingPathComponent:plistName];
    //序列化,把數(shù)據(jù)存入指定目錄的plist文件
    [dict writeToFile:filePath atomically:YES];
}
/**
 根據(jù)plist文件名讀取數(shù)據(jù)
 */
-(NSDictionary *)readFromPlistWithPlistName:(NSString *)plistName{
    //存取路徑
    NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
    //路徑中文件名
    NSString *filePath = [path stringByAppendingPathComponent:plistName];
    NSDictionary *resultDict = [NSDictionary dictionaryWithContentsOfFile:filePath];
    return resultDict;
}

atomically是否先寫入輔助文件,增加安全性的寫入文件方法,一般都是YES

圖1.png

二、preference:偏好設(shè)置

很多iOS應(yīng)用都支持偏好設(shè)置哩陕,比如保存用戶名平项、密碼、字體等設(shè)置悍及。每個應(yīng)用都有NSUserDefaults實例闽瓢,通過它來讀取偏好設(shè)置。一般不要在偏好設(shè)置中保存其他數(shù)據(jù)心赶。
偏好設(shè)置是key-value的方式存取和讀取的扣讼。
使用方法:

/**
 保存數(shù)據(jù)
 */
- (IBAction)saveDate:(UIButton *)sender {
    //獲得NSUserDefaults文件
    NSUserDefaults *userDefaultes = [NSUserDefaults standardUserDefaults];
    //向偏好設(shè)置中寫入內(nèi)容
    [userDefaultes setObject:@"lcf" forKey:@"name"];
    [userDefaultes setInteger:26 forKey:@"age"];
    [userDefaultes setObject:@"boy" forKey:@"sex"];
    //立即同步設(shè)置
    [userDefaultes synchronize];
}
/**
 讀取數(shù)據(jù)
 */
- (IBAction)readDate:(UIButton *)sender {
    //獲得NSUserDefaults文件
    NSUserDefaults *userDefaultes = [NSUserDefaults standardUserDefaults];
    //讀取偏好設(shè)置
    NSString *name = [userDefaultes objectForKey:@"name"];
    NSInteger age = [userDefaultes integerForKey:@"age"];
    NSString *sexStr = [userDefaultes objectForKey:@"sex"];
    NSLog(@"\n%@\n%ld\n%@",name,age,sexStr);
}

注意事項:

  • 如果沒有調(diào)用synchronize方法,系統(tǒng)會根據(jù)I/O情況不定時刻地保存到文件中缨叫。所以如果需要立即寫入椭符,就必須調(diào)用synchronize方法。
  • 偏好設(shè)置會將所有數(shù)據(jù)保存到同一個文件夾弯汰,使用同一個key艰山,會把之前存儲的數(shù)據(jù)覆蓋。

三咏闪、NSKeyedArchiver:歸檔和解檔

歸檔在iOS中是另一種形式的序列化曙搬,只要遵循了NSCoding協(xié)議的對象都可以通過它來實現(xiàn)序列化。由于大多是類都遵循了NSCoding協(xié)議鸽嫂,因此纵装,對于大多數(shù)類來說,歸檔是比較容易實現(xiàn)的据某。

  1. 遵循NSCoding協(xié)議,其中有2個方法是必須實現(xiàn)的:
    1. initWithCoder
    2. encodeWithCoder
//遵循NSCoding協(xié)議
@interface DWSave : NSObject<NSCoding>
/**name*/
@property(nonatomic ,copy)NSString *name;
/**age*/
@property(nonatomic ,assign)NSInteger age;
/**sex*/
@property(nonatomic ,assign)BOOL sex;
@end
//以上內(nèi)容要寫在.h文件中
@implementation DWSave
//歸檔
-(void)encodeWithCoder:(NSCoder *)aCoder{
    [aCoder encodeObject:self.name forKey:@"name"];
    [aCoder encodeInteger:self.age forKey:@"age"];
    [aCoder encodeBool:self.sex forKey:@"sex"];
}
//解檔
-(instancetype)initWithCoder:(NSCoder *)aDecoder;{
    if (self = [super init]) {
        self.name = [aDecoder decodeObjectForKey:@"name"];
        self.age = [aDecoder decodeIntegerForKey:@"age"];
        self.sex = [aDecoder decodeBoolForKey:@"sex"];
    }
    return self;
}
@end

NSKeyedArchiver歸檔

//保存地址
    NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
    //文件名
    NSString *filePath = [path stringByAppendingPathComponent:@"wyp.data"];
    DWSave *save = [[DWSave alloc] init];
    //設(shè)置數(shù)據(jù)
    save.name = @"lcf";
    save.age = 26;
    save.sex = 1;
    [NSKeyedArchiver archiveRootObject:save toFile:filePath];

NSKeyedUnarchiver解檔

NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
    NSString *filePath = [path stringByAppendingPathComponent:@"wyp.data"];
    DWSave *save = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
    if (save) {
        NSLog(@"\n%@\n%ld\n%d",save.name,save.age,save.sex);    
    }

必須要遵循NSCoding協(xié)議
保存文件的擴展名可以自定義
如果需要歸檔的類是某個自定義類的子類時橡娄,就需要在歸檔和解檔之前實現(xiàn)父類的歸檔和解檔方法。[super encodeWithCoder:aCoder][super initWithCoder:aDecoder]方法癣籽。

keychain:鑰匙串

通常情況下挽唉,我們使用NSUserDefaults存儲數(shù)據(jù)信息滤祖,但是對于一些私密信息,但是對于一下比較私密的信息瓶籽,如帳號匠童、密碼等等,我們就需要使用更為安全的keychain了塑顺。keychain保存的信息是保存在沙盒之外的汤求,不會因App的刪除而丟失,在用戶重新安裝了App后依然存在严拒。其實可以把keychain理解成一個Dictionary扬绪,所有數(shù)據(jù)都以key-value的形式存儲,可以對這個Dictionary進行add裤唠、update挤牛、get、delete這四個操作巧骚。對一個應(yīng)用來說赊颠,keychain都有兩個訪問區(qū),私有和公共劈彪。

  1. Target - Capabilities - Keychain Sharing - ON


    圖2.png

    左側(cè)的目錄會自動生成Entitlements文件竣蹦,不需要自己創(chuàng)建了。

  2. 引入Security.framework
  3. 自定義一個類沧奴,取名keychain痘括,如下:

.h文件

#import <Foundation/Foundation.h>
@interface keychain : NSObject
/**添加*/
+(void)savePassWord:(NSString *)password;
/**讀取*/
+(id)loadPassWord;
/**刪除*/
+(void)deletePassword;
@end

.m文件

#import "keychain.h"
#import <Security/Security.h>

@implementation keychain
static NSString *const KEY_KEYCHAIN = @"LCF";
static NSString *const KEY_PASSWORD = @"PASSWORD";
/**添加*/
+(void)savePassWord:(NSString *)password{
    NSMutableDictionary *infoDict = [NSMutableDictionary dictionary];
    [infoDict setObject:password forKey:KEY_PASSWORD];
    [keychain save:KEY_KEYCHAIN data:infoDict];
}
/**讀取*/
+(id)loadPassWord{
    NSMutableDictionary *infoDict = [keychain load:KEY_KEYCHAIN];
    return [infoDict objectForKey:KEY_PASSWORD];
}
/**刪除*/
+(void)deletePassword{
    [self delete:KEY_KEYCHAIN];
}
+(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);
}
@end

kechain部分代碼摘自網(wǎng)絡(luò)

結(jié)束語:
文章中可能會存在錯誤,歡迎 指正滔吠。
Demo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末纲菌,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子疮绷,更是在濱河造成了極大的恐慌翰舌,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件冬骚,死亡現(xiàn)場離奇詭異椅贱,居然都是意外死亡,警方通過查閱死者的電腦和手機只冻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進店門庇麦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人喜德,你說我怎么就攤上這事山橄。” “怎么了舍悯?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵航棱,是天一觀的道長睡雇。 經(jīng)常有香客問我,道長饮醇,這世上最難降的妖魔是什么入桂? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮驳阎,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘馁蒂。我一直安慰自己呵晚,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布沫屡。 她就那樣靜靜地躺著饵隙,像睡著了一般。 火紅的嫁衣襯著肌膚如雪沮脖。 梳的紋絲不亂的頭發(fā)上金矛,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天,我揣著相機與錄音勺届,去河邊找鬼驶俊。 笑死,一個胖子當(dāng)著我的面吹牛免姿,可吹牛的內(nèi)容都是我干的饼酿。 我是一名探鬼主播,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼胚膊,長吁一口氣:“原來是場噩夢啊……” “哼故俐!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起紊婉,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤药版,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后喻犁,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體槽片,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年株汉,在試婚紗的時候發(fā)現(xiàn)自己被綠了筐乳。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡乔妈,死狀恐怖蝙云,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情路召,我是刑警寧澤勃刨,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布波材,位于F島的核電站,受9級特大地震影響身隐,放射性物質(zhì)發(fā)生泄漏廷区。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一贾铝、第九天 我趴在偏房一處隱蔽的房頂上張望隙轻。 院中可真熱鬧,春花似錦垢揩、人聲如沸玖绿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽斑匪。三九已至,卻和暖如春锋勺,著一層夾襖步出監(jiān)牢的瞬間蚀瘸,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工庶橱, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留贮勃,地道東北人。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓悬包,卻偏偏與公主長得像衙猪,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子布近,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內(nèi)容