IOS儲存的五個方案
1. NSUserDefaults (偏好設置文件)
2. plist文件
3. 歸檔 (反歸檔)
4. SQLite3(FMDB)
5. CoreData
沙盒中相關路徑 介紹
- AppName.app 應用程序的程序包目錄,包含應用程序的本身。由于應用程序必須經過簽名,所以不能在運行時對這個目錄中的內容進行修改,否則會導致應用程序無法啟動。
- Documents/ 保存應用程序的重要數據文件和用戶數據文件等。用戶數據基本上都放在這個位置(例如從網上下載的圖片或音樂文件),該文件夾在應用程序更新時會自動備份,在連接iTunes時也可以自動同步備份其中的數據。
- Library:這個目錄下有兩個子目錄,可創建子文件夾??梢杂脕矸胖媚M粋浞莸幌M挥脩艨吹降臄祿?。該路徑下的文件夾,除Caches以外,都會被iTunes備份.
- Library/Caches: 保存應用程序使用時產生的支持文件和緩存文件(保存應用程序再次啟動過程中需要的信息),還有日志文件最好也放在這個目錄。iTunes 同步時不會備份該目錄并且可能被其他工具清理掉其中的數據。
- Library/Preferences: 保存應用程序的偏好設置文件。NSUserDefaults類創建的數據和plist文件都放在這里。會被iTunes備份。
- tmp/: 保存應用運行時所需要的臨時數據。不會被iTunes備份。iPhone重啟時,會被清空。
1. NSUserDefaults
NSUserDefaults 介紹
- 在運行時,您使用對象從用戶的默認數據庫中讀取應用程序使用的默認值。緩存信息,以避免每次需要默認值時都必須打開用戶的默認數據庫。設置默認值時,它將在您的流程中同步更改,并異步更改為持久性存儲和其他流程。
- NSUserDefaults 默認對象必須是一個屬性列表,也就是說,的一個實例(或對集合的實例的組合)NSData,NSString,NSNumber,NSDate,NSArray,或NSDictionary。如果要存儲任何其他類型的對象,通常應將其歸檔以創建NSData的實例。
- NSUserDefaults用來存儲 用戶設置 系統配置等一些小的數據。因為數據是明文存儲在 plist 文件中,不安全,即使只是修改一個 key 都會 load 整個文件,數據多加載慢( 內存),不適合存儲大量數據。
- 它是單例,也是線程安全的,是以鍵值對 key-value 的形式保存在沙盒中
- 沙盒路徑為 Library/Preferences。文件格式為 .plist
- NSUserDefaults返回的值是不可改變的,即使存儲的時候是可變的值。對相同的Key賦值約等于一次覆蓋。
//NSUserDefaults
//setObject中的key和value可以為除了nil外的任何對象
//setValue中的key只能為字符串 value可以為nil也可以為空對象[NSNull null]以及全部對象
NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
[userDefault setObject:@"小明" forKey:@"name"];
//app意外退出或者中斷,數據不會被系統寫入所以命令synchronize直接同步到文件里,來避免數據的丟失。
[userDefault synchronize];
//取出對應的Key也就是name
[userDefault objectForKey:@"name"];
NSLog(@"name = %@",[userDefault objectForKey:@"name"]);
//打印 name = 小明
2. plist文件
plist文件 介紹
- plist的文件名不能單獨命名做"info"、"Info"之類的,是因為與系統屬文件重名
- 屬性列表是一種XML格式的文件,拓展名為plist
- 對象是NSString、NSDictionary、NSArray、NSData、NSNumber等類型,就可以使用writeToFile:atomically:方法直接將對象寫到屬性列表文件中
- Plist不能存儲自定義對象,成功后會寫入到Documents文件中(app)
- xcode中plist文件創建步驟:NewFile —— IOS —— Resource —— Property List
NSString *cachePath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;
NSString *filePath = [cachePath stringByAppendingPathComponent:@"newInfo.plist"];
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setObject:@"小紅" forKey:@"name"];
[dict setObject:@"18" forKey:@"age"];
[dict writeToFile:filePath atomically:YES];
NSDictionary *dics = [NSDictionary dictionaryWithContentsOfFile:filePath];
NSLog(@"age:%@", [dics objectForKey:@"age"]);
newInfo.plist
3. 歸檔 (反歸檔)
歸檔 (反歸檔) 介紹
-之前將數據存儲到本地,只能是字符串、數組、字典、NSNuber、BOOL等容器類對象,不能將自定義對象進行保存,而通過歸檔能將所有的對象轉化為二進制數據存儲到文件中。
- 什么是歸檔
1.字典,數組,自定義的對象等在存儲時需要轉換為字節流NSData類型數據,再通過寫入文件來進行存儲。
- 什么是反歸檔
2.字節流轉換為字典,數組,自定義的類等
- 歸檔的缺點
3.歸檔保存數據,只能一次性歸檔保存以及一次性解壓。所以只能針對小量數據,而且對數據操作比較笨拙,即如果想改動數據的某一小部分,還是需要解壓整個數據或者歸檔整個數據。
- 歸檔的使用場景
4.有些應用沒聯網時,可以將手機有網時的數據存放在本地,沒網時,從本地中取出來這些數據展示。
//Students.h 文件
#import "Students.h"
//遵循NSCoding協議
@interface Students : NSObject<NSCoding>
@property (nonatomic,copy) NSString *name;
@property (nonatomic,assign) NSInteger age;
//Students.m 文件
#import "Students.m"
//需要重寫兩個協議方法
//-(void)encodeWithCoder:(NSCoder *)aCoder方法:
//-(id)initWithCoder:(NSCoder *)aDecoder方法:
// 當將一個自定義對象保存到文件的時候就會調用該方法
// 在該方法中說明如何存儲自定義對象的屬性
// 也就說在該方法中說清楚存儲自定義對象的哪些屬性
-(void)encodeWithCoder:(NSCoder *)aCoder
{
NSLog(@"調用了encodeWithCoder:方法");
[aCoder encodeObject:self.name forKey:@"name"];
[aCoder encodeInteger:self.age forKey:@"age"];
}
// 當從文件中讀取一個對象的時候就會調用該方法
// 在該方法中說明如何讀取保存在文件中的對象
// 也就是說在該方法中說清楚怎么讀取文件中的對象
-(id)initWithCoder:(NSCoder *)aDecoder
{
NSLog(@"調用了initWithCoder:方法");
//注意:在構造方法中需要先初始化父類的方法
if (self=[super init]) {
self.name=[aDecoder decodeObjectForKey:@"name"];
self.age=[aDecoder decodeIntegerForKey:@"age"];
}
return self;
}
//ViewController.h 文件
#import "ViewController.h"
Students *stu = [[Students alloc]init];
stu.name = @"掰掰";
stu.age = 8;
// 獲取文件路徑
NSString *docPath =[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *path = [docPath stringByAppendingPathComponent:@"Students.archiver"];
//進行歸檔
[NSKeyedArchiver archiveRootObject:stu toFile:path];
//反歸檔
Students *s1 = [NSKeyedUnarchiver unarchiveObjectWithFile:path] ;
NSLog(@"---------%@", s1.name);//掰掰
NSLog(@"---------%ld", (long)s1.age);//8
采用多個對象儲存
- (IBAction)archiveManyObject:(id)sender {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentFilePath = paths.firstObject ;
NSString *filePath = [documentFilePath stringByAppendingPathComponent:@"personModel"];
NSMutableData *data = [[NSMutableData alloc] init];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data]; //將數據區連接到NSKeyedArchiver對象
Person *p1 = [[Person alloc] init];
p1.name = @"ran1";
p1.age = @"18";
[archiver encodeObject:p1 forKey:@"person1"];
Person *p2 = [[Person alloc] init];
p2.name = @"ran2";
p2.age = @"19";
[archiver encodeObject:p2 forKey:@"person2"];
[archiver finishEncoding];
[data writeToFile:filePath atomically:YES];
}
- (IBAction)unarchiveManyObject:(id)sender {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentFilePath = paths.firstObject ;
NSString *filePath = [documentFilePath stringByAppendingPathComponent:@"personModel"];
NSData *data = [NSData dataWithContentsOfFile:filePath];
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
Person *p1 = [unarchiver decodeObjectForKey:@"person1"];
Person *p2 = [unarchiver decodeObjectForKey:@"person2"];
[unarchiver finishDecoding];
NSLog(@"%@", p1.name);
NSLog(@"%@", p2.name);
}
4. SQLite3
數據庫(splite):
splite是一個輕量級,跨平臺的小型數據庫,可移植性比較高,有著和MySpl幾乎相同的數據庫語句,以及無需服務器即可使用的優點:
數據庫的優點:
存儲大量的數據,存儲和檢索的速度非???
能對數據進行大量的聚合,這樣比起使用對象來講操作要快.
數據庫的缺點:
它沒有提供數據庫的創建方式
它的底層是基于C語言框架設計的, 沒有面向對象的API, 用起來非常麻煩
發雜的數據模型的數據建表,非常麻煩
在實際開發中我們都是使用的是FMDB第三方開源的數據庫,該數據庫是基于splite封裝的面向對象的框架.
//sqlite3
//1.獲取沙盒文件名
NSString *fileName = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"student.sqlite"];
NSLog(@"fileName = %@",fileName);
int result = sqlite3_open(fileName.UTF8String, &_db); //創建(打開)數據庫,如果數據庫不存在,會自動創建 數據庫文件的路徑必須以C字符串(而非NSString)傳入
if (result == SQLITE_OK) {
NSLog(@"成功打開數據庫");
char *errorMesg = NULL;
const char *sql = "create table if not exists t_person (id integer primary key autoincrement, name text, age integer);";
int result = sqlite3_exec(_db, sql, NULL, NULL, &errorMesg); //sqlite3_exec()可以執行任何SQL語句,比如創表、更新、插入和刪除操作。但是一般不用它執行查詢語句,因為它不會返回查詢到的數據
if (result == SQLITE_OK) {
NSLog(@"成功創建t_person表");
} else {
NSLog(@"創建t_person表失敗:%s",errorMesg);
}
} else {
NSLog(@"打開數據庫失敗");
}
//sqlite3 添加數據庫
- (IBAction)AddSqlite3:(UIButton *)sender {
for (int i = 0; i < 10; i++) {
NSString *name = [NSString stringWithFormat:@"小明-%d",arc4random()%100];
int age = arc4random() % 100;
char *errorMesg = NULL;
NSString *sql = [NSString stringWithFormat:@"insert into t_person (name,age) values ('%@',%d);",name, age];
int result = sqlite3_exec(_db, sql.UTF8String, NULL, NULL, &errorMesg);
if (result == SQLITE_OK) {
NSLog(@"添加數據成功");
} else {
NSLog(@"添加數據失敗");
}
}
}
//刪除數據庫
- (IBAction)sqlite3delete:(id)sender
{
char *errorMesg = NULL;
NSString *sql = @"delete from t_person where age >= 0";
int result = sqlite3_exec(_db, sql.UTF8String, NULL, NULL, &errorMesg);
if (result == SQLITE_OK) {
NSLog(@"刪除成功");
}else {
NSLog(@"刪除失敗");
}
}
//查詢數據庫
- (IBAction)query:(id)sender
{
const char *sql = "select id, name, age from t_person;"; //"select id, name, age from t_person where age >= 50;"
sqlite3_stmt *stmt = NULL; //定義一個stmt存放結果集
int result = sqlite3_prepare_v2(_db, sql, -1, &stmt, NULL); //檢測SQL語句的合法性
if (result == SQLITE_OK) {
NSLog(@"查詢語句合法");
while (sqlite3_step(stmt) == SQLITE_ROW) {
int ID = sqlite3_column_int(stmt, 0);
const unsigned char *sname = sqlite3_column_text(stmt, 1);
NSString *name = [NSString stringWithUTF8String:(const char *)sname];
int age = sqlite3_column_int(stmt, 2);
NSLog(@"%d %@ %d",ID, name, age);
}
} else {
NSLog(@"查詢語句非法");
}
}
//修改數據庫
- (IBAction)sqliteUpdate:(id)sender {
NSString *sql = @"update t_person set name = '哈哈' where age > 60";
char *errorMesg = NULL;
int result = sqlite3_exec(_db, sql.UTF8String, NULL, NULL, &errorMesg);
if (result == SQLITE_OK) {
NSLog(@"更改成功");
}else {
NSLog(@"更改失敗");
}
}
5. CoreData
CoreData核心類與結構
NSManagedObjectContext(數據上下文)
對象管理上下文,負責數據的實際操作(重要)
作用:插入數據,查詢數據,刪除數據,更新數據
NSPersistentStoreCoordinator(持久化存儲助理)
相當于數據庫的連接器
作用:設置數據存儲的名字,位置,存儲方式,和存儲時機
NSManagedObjectModel(數據模型)
數據庫所有表格或數據結構,包含各實體的定義信息
作用:添加實體的屬性,建立屬性之間的關系
操作方法:視圖編輯器,或代碼
NSManagedObject(被管理的數據記錄)
數據庫中的表格記錄
NSEntityDescription(實體結構)
相當于表格結構
NSFetchRequest(數據請求)
相當于查詢語句
后綴為.xcdatamodeld的包
里面是.xcdatamodel文件,用數據模型編輯器編輯
編譯后為.momd或.mom文件