之前由於剛入行不久,對數據持久化不是很了解,尤其是用數據庫存儲大量數據的操作。經過摸索就在此總結一下,方便以後查閱
下面就簡單介紹一下:
感覺最常用的小量數據,屬性,例如,賬號,密碼之類的;適合存儲輕量級的本地數據 (個人認為這種比較簡單)
NSUserDefaults支持的數據格式有:NSNumber(Integer、Float、Double),NSString,NSDate,NSArray,NSDictionary,BOOL類型
直接看代碼比較容易理解:
// 測試數據
NSArray *myArray = [NSArray arrayWithObjects:@"hello", @"world", nil];
NSDictionary *myDictionary = [NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:@"enuo", @"20", nil] forKeys:[NSArray arrayWithObjects:@"name", @"age", nil]];
// 存入
[[NSUserDefaults standardUserDefaults] setObject:myArray forKey:@"arry"];
[[NSUserDefaults standardUserDefaults] setObject:myDictionary forKey:@"dict"];
//這裡建議同步存儲到磁盤中,但是不是必須的
[[NSUserDefaults standardUserDefaults] synchronize];
// 讀取
NSArray *arr= [[NSUserDefaults standardUserDefaults] objectForKey:@"arry"];
NSDictionary *dict = [[NSUserDefaults standardUserDefaults] objectForKey:@"dict"];
NSLog(@"------%@----\n-----%@------",arr,dict);
// 清除數據
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"keyName"];
建議:[NSUserDefaults standardUserDefaults] 可以在項目中定義一個全局的宏,這樣使用起來更便捷。
目的是為了長時間存放有組織的數據集
// (1)歸檔
NSArray *array = [NSArray arrayWithObjects:@1,@2,@3, nil];
NSString *filePath = [NSHomeDirectory() stringByAppendingString:@"testFile.plist"];
BOOL success = [NSKeyedArchiver archiveRootObject:array toFile:filePath];
NSLog(@"%d",success);
NSLog(@"%@",filePath);
// (2)反歸檔
NSString *filePath = [NSHomeDirectory() stringByAppendingString:@"testFile.plist"];
id OUTarray = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
NSLog(@"%@",OUTarray);
// (1)歸檔
NSMutableArray *arrayArchiver = [NSMutableArray arrayWithObjects:@"BigDragon",@"BigBiao",@"BigBaby", nil];
NSMutableData *data = [NSMutableData data];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
//編碼
[archiver encodeObject:arrayArchiver forKey:@"array"];
[archiver encodeObject:@"Jason‘s friends" forKey:@"name"];
//編碼完成之後,對象已經存儲到data之中。
[archiver finishEncoding];
NSString *filePathtwo = [NSHomeDirectory() stringByAppendingPathComponent:@"arraytwo.plist"];
BOOL successtwo = [data writeToFile:filePathtwo atomically:YES];
NSLog(@"------%d",successtwo);
// (2)反歸檔
NSString *filePathtwo = [NSHomeDirectory() stringByAppendingPathComponent:@"arraytwo.plist"];
//讀取歸檔數據
NSData *dataUnarchiver = [[NSData alloc] initWithContentsOfFile:filePathtwo];
//創建解歸檔對象,進行反歸檔
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:dataUnarchiver];
//反歸檔
NSArray *arrayUnarchiver = [unarchiver decodeObjectForKey:@"array"];
NSLog(@"----%@",arrayUnarchiver);
NSString *nameUnarchiver = [unarchiver decodeObjectForKey:@"name"];
NSLog(@"-----%@",nameUnarchiver);
下面就先建個工具類;Person類
Person.h
#import <Foundation/Foundation.h> @interface Person : NSObject<NSCoding> //需要遵守NSCoding協議 @property (nonatomic,strong) NSString *name; //帶歸檔類型 @property (nonatomic,assign) NSInteger age; @property (nonatomic,strong) NSString *gender; - (NSString *)description; @end
Person.m
/**
該對象對應的類必須提供encodeWithCoder:和initWithCoder:方法。前一個方法告訴系統怎麼對對象進行編碼,而後一個方法則是告訴系統怎麼對對象進行解碼
*/
// 歸檔方法
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:self.name forKey:@"name"];
[aCoder encodeInteger:self.age forKey:@"age"];
[aCoder encodeObject:self.gender forKey:@"gender"];
}
// 反歸檔方法
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil) {
self.name = [aDecoder decodeObjectForKey:@"name"];
self.age = [aDecoder decodeIntegerForKey:@"age"];
self.gender = [aDecoder decodeObjectForKey:@"gender"];
}
return self;
}
- (NSString *)description
{
NSString *string = [NSString stringWithFormat:@"%@\n %ld\n %@",self.name,self.age,self.gender];
return string;
}
在控制器用的時候:
Person *person = [[Person alloc]init];
person.name = @"rose";
person.age = 25;
person.gender = @"man";
// 歸檔,調用歸檔方法
NSString *filePath3 = [NSHomeDirectory() stringByAppendingString:@"person.plist"];
BOOL success3 = [NSKeyedArchiver archiveRootObject:person toFile:filePath3];
NSLog(@"=====%d",success3);
// 反歸檔,調用反歸檔方法
Person *per = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath3];
NSLog(@"=======%@",per);
主要的步驟:
//第一步:獲得文件即將保存的路徑:(永久保存在磁盤中)
// 文件存儲的相對目錄
NSArray *filePaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString*path = filePaths.firstObject;
//第二步:生成在該路徑下的文件:
NSString *FileName=[path stringByAppendingPathComponent:@"flieName"];//fileName就是保存文件的文件名
// 第三步:往文件中寫入數據:
NSData *data;
[data writeToFile:FileName atomically:YES];//將NSData類型對象data寫入文件,文件名為FileName
// 最後:從文件中讀出數據:
NSData *outdata=[NSData dataWithContentsOfFile:FileName options:0 error:NULL];//從FileName中讀取出數據
下面就簡單的舉幾個例子
#pragma mark--1.字符串寫入
// NSString *strfilepath = [path stringByAppendingPathComponent:@"str.text"];
NSString *strfilepath = [path stringByAppendingString:@"/text.text"];
NSString *INstr = @"字符串寫入文件";
[INstr writeToFile:strfilepath atomically:YES encoding:NSUTF8StringEncoding error:nil];
// 讀取文件
NSString *OUTstr = [[NSString alloc]initWithContentsOfFile:strfilepath encoding:NSUTF8StringEncoding error:nil];
NSLog(@"----%@-----",OUTstr);
#pragma mark-2、數組對象寫入文件
// 構造數組plist文件的存儲路徑
NSString*arrypath = [path stringByAppendingPathComponent:@"arry.plist"];
NSArray *INarr = @[@"hahah",@"hehhe",@"heihei"];
[INarr writeToFile:arrypath atomically:YES];
// 讀取文件
NSArray *outarr = [[NSArray array]initWithContentsOfFile:arrypath];
NSLog(@"----%@-----",outarr);
#pragma mark-3、字典對象寫入文件
NSString*dictpath = [path stringByAppendingPathComponent:@"dict.plist"];
NSDictionary *dict = @{@"姓名":@"liuwenqiang",@"年齡":@"25",@"職業":@"ios"};
[dict writeToFile:dictpath atomically:YES];
// 讀取文件
NSDictionary *OUTdict = [[NSDictionary alloc]initWithContentsOfFile:dictpath];
NSLog(@"----%@-----",OUTdict);
#pragma mark-4、二進制對象寫入文件
NSString *datapath = [path stringByAppendingPathComponent:@"song.m4a"];
UIImage *image = [UIImage imageNamed:@"image"];
NSData *imagedata = UIImageJPEGRepresentation(image, 1);
[imagedata writeToFile:datapath atomically:YES];
// 讀取文件
NSData *OUTdata = [[NSData alloc]initWithContentsOfFile:datapath];
NSLog(@"----%@-----",OUTdata);
對於大型的數據存儲,再使用上面的方法顯然不適用了,該使用數據庫了,我在項目中使用的是第三方的數據庫FMDB,個人感覺比較好用,使用比較順手。
那麼什麼是大型數據,我認為的就是那種像app裡的列表之類的,由於列表的數據量相當比較大,如果像我們的應用,列表中還有列表圖的話,那就更大了。
當初在看數據庫之前,總感覺很難理解,不好學,但是慢慢的理解了,也並不是那麼難理解,(下面的方法是需要在工具類的.h中聲明的,這樣控制器中才能調到)
在操作之前,需要先下載一個FMDB 拉到項目中 附上: FMDB下載地址
先創建一個工具類 fmbdTool
@implementation FmdbTool
{
FMDatabase *db;
}
-(NSString*)createFMDBTable:(NSString *)type
{
NSString *str;
//1.獲得數據庫文件的路徑
NSString *doctorpath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString *filePath = [doctorpath stringByAppendingPathComponent:@"doctor.sqlite"];
// NSLog(@"-------路徑----%@-------",filePath);
//2.獲得數據庫
db = [FMDatabase databaseWithPath:filePath];
//3.打開數據庫
if ([db open]) {
//4.創表
BOOL result;
if ([type isEqualToString:@"doctor"]) {
result = [db executeUpdate:@"CREATE TABLE IF NOT EXISTS t_doctor (id integer PRIMARY KEY AUTOINCREMENT,doctordict blob NOT NULL,page_ID integer NOT NULL,datacount text NOT NULL)"];
}
if (result) {
// NSLog(@"====建表成功--------type=== %@",type);
str = @"yes";
}else
{
NSLog(@"====建表失敗");
str = @"no";
}
}else {
NSLog(@"faile to create a FMDB");
str = @"no";
}
return str;
}
在我的項目中,由於有很多的列表要做數據存儲,因此我在創建庫的方法中加了一個參數type,為每個要存儲的頁面都建一張表,並把所有的表都放到
doctor.sqlite這個庫中。
在創建表的時候,首先要[db open]打開數據庫,該返回數據類型是bool,當為真的時候,再創建表
result = [db executeUpdate:@"CREATE TABLE IF NOT EXISTS t_doctor (id integer PRIMARY KEY AUTOINCREMENT,doctordict blob NOT NULL,page_ID integer NOT NULL,datacount text NOT NULL)"];
doctordict是個數字類型的數據,就是像後台返回的下面數據中,doctorInfoList字段,所以doctordict的類型是blob 。並且由於該字段不可能為空,故在建表的時候 NOT NULL 。
列表做了分頁請求,所以在表中把頁碼存儲了,page_ID integer NOT NULL。 datacount是列表總的頁數。

//插數據
-(void)addDataInsetTable:(NSArray *)doctorListArry page:(NSInteger)page datacount:(NSString*)datacount type:(NSString *)type
{
if ([db open]) {
NSString *pagestr = [NSString stringWithFormat:@"%ld",(long)page];
NSError *err = nil;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:doctorListArry options:NSJSONWritingPrettyPrinted error:&err];
NSString *jsonStr = [[NSString alloc]initWithData:jsonData encoding:NSUTF8StringEncoding];
if ([type isEqualToString:@"doctor"]) {
[db executeUpdate:@"INSERT INTO t_doctor (doctordict,page_ID,datacount) VALUES (?,?,?);",jsonStr,pagestr,datacount];
}
}
[db open];
}
插入數據是也是 先[db open]之後才能插入數據。
通過這個方法可以看出,我直接就可以把後台返回給我的數據,responseObject[@"doctorInfoList"] 插到表中,
在插入之前,需要先把數組轉成json類型的字符串,我在這裡使用了,JSONKit JSONKit下載地址
-(NSArray *)outdata:(NSString *)type
{
NSArray *dataArr;
NSMutableArray *aaarr = [[NSMutableArray alloc]init];
if ([db open]) {
if ([type isEqualToString:@"doctor"]) {
FMResultSet *res = [db executeQuery:@"SELECT * FROM t_doctor"];
while (res.next) {
NSString *s = [res stringForColumn:@"doctordict"];
NSArray *aa = [s objectFromJSONString];
[aaarr addObjectsFromArray:aa];
dataArr = aaarr;
}
}
}
[db close];
return dataArr;
}
//取出當前頁數 在該type時
-(NSString *)checkoutpage:(NSString *)type
{
NSString *str;
if ([db open]) {
FMResultSet *res = [db executeQuery:@"SELECT * FROM t_activesecond where type = ?",type];
while (res.next)
{
str = [res stringForColumn:@"page"];
}
}
[db close];
return str;
}
//刪除數據
- (void)deleteData:(NSString *)type
{
if ([db open]) {
if ([type isEqualToString:@"doctor"]) {
NSString *deleteSql = [NSString stringWithFormat:@"delete from t_doctor"];
[db executeUpdate:deleteSql];
}
}
[db close];
}
我在刪除數據的時候,只刪除了,指定表,並沒有直接把庫刪了,因為所以的表都在這個庫裡。
先寫到這吧,如果哪裡說錯了,請指出,謝謝啦。