最近在學(xué)習(xí)在iOS開發(fā)中如何做數(shù)據(jù)的持久化。我們?cè)陂_發(fā)APP時(shí)有時(shí)需要將數(shù)據(jù)保存到本地。下面總結(jié)一下幾種數(shù)據(jù)持久化的方式及其應(yīng)用。這只是我的學(xué)習(xí)筆記,有錯(cuò)誤的地方還望指點(diǎn)。
數(shù)據(jù)持久化的幾種方式:
a.XML屬性列表(plist)
b.Preference(偏好設(shè)置)
c.NSKeyedArchiver歸檔(NSCoding)
d.SQLite
e.Core Data
這篇筆記先總結(jié)一下前三種(plist、偏好設(shè)置、歸檔)。后面兩種(SQLite、Core Data)在下篇總結(jié)。在介紹之前先了解一下沙盒(sandbox)結(jié)構(gòu)。
我們通過(guò)如下代碼:
NSString *homePath = NSHomeDirectory();
NSLog(@"%@",homePath);
可以獲取到沙盒路徑。
然后將路徑貼到Finder前往文件夾中就可以看到沙盒中如下結(jié)構(gòu)
Documents:保存應(yīng)用運(yùn)行時(shí)生成的需要持久化的數(shù)據(jù),iTunes同步設(shè)備時(shí)會(huì)備份該目錄。例如,游戲應(yīng)用可將游戲存檔保存在該目錄,還有我們的通訊錄之類的。
Library/Caches:保存應(yīng)用運(yùn)行時(shí)生成的需要持久化的數(shù)據(jù),iTunes同步設(shè)備時(shí)不會(huì)備份該目錄。一般存儲(chǔ)體積大、不需要備份的非重要數(shù)據(jù)。比如我們朋友圈緩存的照片。
Library/Preference:保存應(yīng)用的所有偏好設(shè)置,iOS的Settings(設(shè)置)應(yīng)用會(huì)在該目錄中查找應(yīng)用的設(shè)置信息。iTunes同步設(shè)備時(shí)會(huì)備份該目錄。比如我們?cè)O(shè)置的字體、皮膚、賬戶信息等。
tmp:保存應(yīng)用運(yùn)行時(shí)所需的臨時(shí)數(shù)據(jù),使用完畢后再將相應(yīng)的文件從該目錄刪除。應(yīng)用沒(méi)有運(yùn)行時(shí),系統(tǒng)也可能會(huì)清除該目錄下的文件。iTunes同步設(shè)備時(shí)不會(huì)備份該目錄。
1.路徑獲取
(1) Documents
方式1:
documenstPath = [homePath stringByAppendingPathComponent:@"Documents"];
方式2:
documenstPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
(2) 獲取Caches路徑
NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
//參數(shù):YES:全路徑 NO:相對(duì)路徑
注:Preference路徑不能用搜索的方式去獲取,可以通過(guò)拼接路徑獲取到,但是蘋果不希望我們這樣做。
要使用偏好設(shè)置,是這樣獲取得的
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
(3) 獲取tmp路徑
NSString *tmpPath = NSTemporaryDirectory();
2.plist的使用
plist文件保存數(shù)組
// 1.創(chuàng)建數(shù)組
NSArray *array = @[@"1",@"2",@"3"];
// 2.獲取Documents路徑 沙盒/Documents
NSString *documents = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
// 3.拼接文件路徑 沙盒/Documents/array.plist
NSString *filePath = [documents stringByAppendingPathComponent:@"array.plist"];
// 4.寫入文件
BOOL success = [array writeToFile:filePath atomically:YES];
//atomically:線性安全的
讀取保存好的數(shù)組
// 1.獲取documents路徑
NSString *documents = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
//2.拼接文件路徑
NSString *filePath = [documents stringByAppendingPathComponent:@"array.plist"];
// 2.讀取數(shù)組
NSArray *array = [NSArray arrayWithContentsOfFile:filePath];
plist文件保存字典
// 1.創(chuàng)建字典
NSDictionary *dict = @{@"a":@"1",@"b":@"2",@"c":@"3"};
// 2.獲取Documents路徑 沙盒/Documents
NSString *documents = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
// 3.拼接文件路徑 沙盒/Documents/dict.plist
NSString *filePath = [documents stringByAppendingPathComponent:@"dict.plist"];
// 4.寫入文件
[dict writeToFile:filePath atomically:YES];
讀取保存好的字典
// 1.獲取documents路徑
NSString *documents = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
//2.拼接文件路徑
NSString *filePath = [documents stringByAppendingPathComponent:@"dict.plist"];
// 3.讀取字典
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:filePath];
2.Preference(偏好設(shè)置)
// 1.獲取偏好設(shè)置 default/standard/share/main
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
// 2.保存數(shù)據(jù)
[defaults setObject:@"仰望星空" forKey:@"name"];
[defaults setBool:YES forKey:@"boy"];
// 3.讀取數(shù)據(jù)
NSString *name = [defaults objectForKey:@"name"];
BOOL sex = [defaults boolForKey:@"boy"];
NSLog(@"%@ %@",name,sex?@"YES":@"NO");
// iOS8之前 需要同步
[defaults synchronize];
Preference(偏好設(shè)置) 是通過(guò) set 。。。forKey。。。來(lái)保存數(shù)據(jù)的
一般有以下幾種方式
讀取也是通過(guò)相應(yīng)的方法來(lái)讀取
[defaults objectForKey:@"name"];
[defaults boolForKey:@"boy"];
3.NSKeyedArchiver歸檔(NSCoding)
在iOS開發(fā)中,經(jīng)常需要保持一些對(duì)象,屬性列表和偏好設(shè)置均不能實(shí)現(xiàn)。針對(duì)這種情況,iOS提供了對(duì)象歸檔技術(shù)。它采用序列化的方式,實(shí)現(xiàn)對(duì)象的存儲(chǔ)。通常來(lái)說(shuō)對(duì)象歸檔的操作有兩個(gè)方面。具體如下:
對(duì)象歸檔:以一種不可讀的方式,將對(duì)象寫入到指定文件中。
對(duì)象反歸檔:從指定文件中讀取數(shù)據(jù),并自動(dòng)的重建對(duì)象。
對(duì)于這兩種情況,iOS提供了相應(yīng)的類,實(shí)現(xiàn)對(duì)象的歸檔和反歸檔,具體如下:
1.NSKeyedArchiver 類
NSKeyedArchiver 類直接繼承NSCoder類,將對(duì)象歸檔到指定的文件中,該類提供了兩個(gè)方法。
+ (NSData *)archivedDataWithRootObject:(id)rootObject;
+ (BOOL)archiveRootObject:(id)rootObject toFile:(NSString *)path;
均是類方法無(wú)需創(chuàng)建對(duì)象,直接使用類名NSKeyedArchiver 調(diào)用就可以了。其中 archiveRootObject 方法需要傳入一個(gè)路徑參數(shù),該參數(shù)用于指定對(duì)象保存路徑。
2.NSKeyedUnarchiver 類
NSKeyedUnarchiver 類也是直接繼承NSCoder類,負(fù)責(zé)從文件中回復(fù)對(duì)象,該類提供了兩個(gè)方法,具體格式如下:
+ (nullable id)unarchiveObjectWithData:(NSData *)data;
+ (nullable id)unarchiveObjectWithFile:(NSString *)path;
unarchiveObjectWithFile 方法也是需要傳入一個(gè)路徑參數(shù)。
NSCoding協(xié)議
在對(duì)象的歸檔技術(shù)中,如果對(duì)象要?dú)w檔需要遵守NSCoding協(xié)議,凡是遵守了NSCoding協(xié)議的自定義對(duì)象,都可以實(shí)現(xiàn)對(duì)象的歸檔和反歸檔。NSCoding協(xié)議中定義了兩個(gè)方法,這兩個(gè)方法是對(duì)象歸檔必須實(shí)現(xiàn)的。
- (void)encodeWithCoder:(NSCoder *)aCoder; //該方法負(fù)責(zé)歸檔該對(duì)象的所有是咧變量
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder; //該方法負(fù)責(zé)恢復(fù)該對(duì)象的實(shí)例變量的值
所以通過(guò)重寫這兩個(gè)方法,來(lái)指定如何歸檔和回復(fù)對(duì)象的每個(gè)實(shí)例變量,為此,NSCoder類提供了相應(yīng)的方法來(lái)實(shí)現(xiàn)對(duì)象的歸檔、恢復(fù)對(duì)象每個(gè)實(shí)例變量的方法。
常用的歸檔數(shù)據(jù)的方法
- (void)encodeObject:(nullable id)objv forKey:(NSString *)key; //將Object類型編碼,使其與字符串類型的鍵相關(guān)聯(lián)
- (void)encodeBool:(BOOL)boolv forKey:(NSString *)key; //將BOOL類型編碼,使其與字符串類型的鍵相關(guān)聯(lián)
- (void)encodeInt:(int)intv forKey:(NSString *)key; //將int類型編碼,使其與字符串類型的鍵相關(guān)聯(lián)
- (void)encodeFloat:(float)realv forKey:(NSString *)key; //將float類型編碼,使其與字符串類型的鍵相關(guān)聯(lián)
- (void)encodeDouble:(double)realv forKey:(NSString *)key; //將double類型編碼,使其與字符串類型的鍵相關(guān)聯(lián)
常用的恢復(fù)對(duì)象實(shí)例變量的方法
- (nullable id)decodeObjectForKey:(NSString *)key; //解碼并返回一個(gè)與給定鍵相關(guān)聯(lián)的Object類型的值
- (BOOL)decodeBoolForKey:(NSString *)key; //解碼并返回一個(gè)與給定鍵相關(guān)聯(lián)的Bool類型的值
- (int)decodeIntForKey:(NSString *)key; //解碼并返回一個(gè)與給定鍵相關(guān)聯(lián)的int類型的值
- (float)decodeFloatForKey:(NSString *)key; //解碼并返回一個(gè)與給定鍵相關(guān)聯(lián)的float類型的值
- (double)decodeDoubleForKey:(NSString *)key; //解碼并返回一個(gè)與給定鍵相關(guān)聯(lián)的double類型的值
使用歸檔保存對(duì)象,應(yīng)用如下:
我們創(chuàng)建一個(gè)Person類
Person.h文件中
@interface Person : NSObject <NSCoding>
//定義了三個(gè)屬性
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) int age;
@property (nonatomic, assign) float height;
@end
Person.m文件中
#import "Person.h"
@implementation Person
//將一個(gè)自定義歸檔時(shí)就會(huì)調(diào)用該方法,該方法用于描述如何存儲(chǔ)自定義對(duì)象的屬性
-(void)encodeWithCoder:(NSCoder *)aCoder{
[aCoder encodeObject:self.name forKey:@"Name"];
[aCoder encodeInt:self.age forKey:@"Age"];
[aCoder encodeFloat:self.height forKey:@"Height"];
}
//從文件讀取一個(gè)對(duì)象的時(shí)候會(huì)調(diào)用該方法,該方法用于描述如何讀取保存在文件中的數(shù)據(jù)
-(instancetype)initWithCoder:(NSCoder *)aDecoder{
if (self = [super init]) {
self.name = [aDecoder decodeObjectForKey:@"Name"];
self.age = [aDecoder decodeIntForKey:@"Age"];
self.height = [aDecoder decodeFloatForKey:@"Height"];
}
return self;
}
//重寫description方法,打印對(duì)象的時(shí)候,就可以將其所有屬性打印出來(lái)
-(NSString *)description{
return [NSString stringWithFormat:@"name = %@, age = %d height = %0.1f",_name,_age,_height];
}
ViewController.h 文件中
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@end
ViewController.m 文件中
#import "ViewController.h"
#import "Person.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
//單擊“保存”按鈕執(zhí)行的方法
-(IBAction)save{
//1.創(chuàng)建對(duì)象
Person *person = [[Person alloc]init];
person.name = @"Rose";
person.age = 18;
person.height = 48;
//2.獲取 Documents 路徑
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject];
//3.拼接將要保存文件的路徑,文件名可以隨便取 后綴也可以隨便給
NSString *filePath = [path stringByAppendingPathComponent:@"person.data"];
// NSLog(@"%@",filePath);
//4.將對(duì)象 person 歸檔
[NSKeyedArchiver archiveRootObject:person toFile:filePath];
}
//單擊“讀取”按鈕執(zhí)行的方法
-(IBAction)read{
//1.獲取 Documents 路徑
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject];
//2.拼接保存的文件路徑
NSString *filePath = [path stringByAppendingPathComponent:@"person.data"];
//3.從指定的文件中讀取對(duì)象
Person *person = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
NSLog(@"%@",person);
}
@end
點(diǎn)擊“保存”按鈕后,Documents 文件家中出現(xiàn)了“person.data”文件
點(diǎn)擊“讀取”按鈕后,成功打印了person 對(duì)象屬性,說(shuō)明保存和讀取數(shù)據(jù)都是成功的。
數(shù)據(jù)持久化的前三種方式(plist、偏好設(shè)置、歸檔)總結(jié)完了,后面兩種(SQLite、Core Data)將在下篇總結(jié)。