IOS---數據持久化

##一、文件管理ios

ios沙盒機制:git

ios應用程序只能在爲該程序建立的文件系統中讀取文件,不能夠去其餘地方訪問,此區域被稱爲沙盒,因此全部的非代碼文件都要保存在此,例如圖像、圖標、聲音、映像、屬性列表、文本文件等。github

  • 每一個應用程序都有本身的存儲空間
  • 應用程序不能翻過本身的圍牆去訪問別的存儲空間的內容
  • 應用程序請求的數據都要經過權限檢測,假如不符合條件的話,不會被放行。

###1.1 目錄結構及獲取:sql

沙盒路徑目錄獲取:數據庫

NSString *path = NSHomeDirectory();
NSLog(@"%@",path);

默認狀況下,每一個沙盒含有3個⽂件夾:Documents, Library 和 tmp。由於應⽤的沙盒機制,應用只能在幾個目錄下讀寫文件編程

Documents:蘋果建議將程序中創建的或在程序中瀏覽到的文件數據保存在該⺫錄下,iTunes備份和恢復的時候會包括此目錄,基於NSUserDefaults的首選項的設置除外。數組

對於Document文件夾目錄路徑的獲取,API提供了這麼一種方法:緩存

NSString *documents = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    NSLog(@"%@",documents);

Library:存儲程序的默認設置或其它狀態信息;(iTunes備份和恢復的時候會包含此目錄) Library/Caches:存放緩存⽂件,iTunes不會備份此⺫錄,此目錄下文件不會在應⽤退出刪除安全

獲取Library目錄路徑:框架

NSString *library = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject];
    NSLog(@"%@",library);

獲取緩存目錄Caches路徑:

NSString *cacher = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
    NSLog(@"%@",cacher);

tmp:提供一個即時建立臨時⽂件的地⽅方。(系統重啓後會刪除tmp裏的全部文件)

獲取Tem目錄路徑:

NSString *tem = NSTemporaryDirectory();
    NSLog(@"%@",tem);

ps:能夠用字符串方法拼接一個目錄路徑,會自動加「/」,示例獲取Documents 目錄路徑:

NSString *docPath = [path stringByAppendingPathComponent:@"Documents"];
    NSLog(@"%@",docPath);

###1.2 文件管理類(NSFileManager):用於管理(增、刪、改、查)沙盒目錄文件

建立目錄

1.建立NSFileManager對象並獲取Document路徑:

NSFileManager *fileManager = [NSFileManager defaultManager];
- (NSString *)docPath {
    
    return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
}

2.在Document目錄裏面添加一個新的目錄路徑

NSString *dataPath = [[self docPath] stringByAppendingPathComponent:@"Datas"];

3.使用NSFileManager建立目錄,返回的是一個BOOL值

NSError *error = nil;
    BOOL isCreated = [fileManager createDirectoryAtPath:dataPath withIntermediateDirectories:NO attributes:nil error:&error];
    
    if (isCreated) {
        NSLog(@"建立目錄成功");
    } else {        
        NSLog(@"建立目錄失敗");
    }

建立文件並寫入數據,能夠寫入任意類型的文件,圖片,文字,數組等

1.根據獲取的路徑在裏面新建一個路徑/文件:

NSString *filePath = [[self docPath] stringByAppendingPathComponent:@"Datas/info.png"];

2.建立須要寫入的文件:

// NSString *text = @"這是保存的數據";
    // NSData *data = [text dataUsingEncoding:NSUTF8StringEncoding];
    
    UIImage *image = [UIImage imageNamed:@"足球"];
    NSData *imageData = UIImagePNGRepresentation(image);

3.建立文件:

BOOL isCreated = [fileManager createFileAtPath:filePath contents:imageData attributes:nil];
    
    if (isCreated) {
        NSLog(@"建立成功");      
        NSLog(@"%@",[self docPath]);
    } else {        
        NSLog(@"建立失敗");
    }

根據指定的路徑移除文件、目錄 1.建立須要移除的文件路徑:

NSString *filePath = [[self docPath] stringByAppendingPathComponent:@"Datas/info.png"];

2.移除文件:

BOOL isRemoved = [fileManager removeItemAtPath:filePath error:nil];
    
    if (isRemoved) {
        NSLog(@"文件移除成功");
        
    } else {
        NSLog(@"文件移除失敗");
    }

3.移動文件: 建立要移動文件路徑,和要移動到文件的路徑

NSString *filePath = [[self docPath] stringByAppendingPathComponent:@"Datas/info.png"];
    
    NSString *toPath = [[self libPath] stringByAppendingPathComponent:@"足球.png"];
- (NSString *)libPath {
    
    return [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject];
}

移動文件:

BOOL isMoved = [fileManager moveItemAtPath:filePath toPath:toPath error:nil];
    
    if (isMoved) {
        NSLog(@"移動完成");
        NSLog(@"%@",[self docPath]);
    } else {        
        NSLog(@"移動失敗");
    }

##二、屬性列表

屬性列表是一種明文的輕量級存儲方式,其存儲格式有多種,最常規格式爲XML格式。在咱們建立一個新的項目的時候,Xcode會自動生成一個info.plist文件用來存儲項目的部分系統設置。plist只能用數組(NSArray)或者字典(NSDictionary)進行讀取,因爲屬性列表自己不加密,因此安全性幾乎能夠說爲零。由於,屬性列表正經常使用於存儲少許的而且不重要的數據。

使⽤屬性列表持久化數據對象很是方便,只要是數組或者字典中包含的對象是特定的 可序列話對象(包括NSArray,NSMutableArray,NSDictionary, NSMutableDictionary,NSData,NSMutableData,NSString,NSMutableString, NSNumber,NSDate),就能夠直接經過NSDictionary和NSArray的實例方法將其保存到屬性列表或者從屬性列表中讀取他們。

NSArray類方法:

+ arrayWithContentsOfFile:靜態建立工廠方法,用於從屬性列表中數據,建立NSArray對象。

獲取本地路徑的plist文件:

NSArray *infos_ = [NSArray arrayWithContentsOfFile:filePath_array];

- initWithContentsOfFile:構建函數,用於從屬性列表中數據,建立NSArray對象。

- writeToFile:atomically:該方法把NSArray對象寫入屬性列表文件,它的第一個參數是文件名,第二個參數代表是否使用輔助文件:若是是ture,則先寫入輔助文件,而後將輔助文件從新命名爲目標文件;若是爲false,則直接寫入目標文件。

- writeToFile:options:error:經過提供寫入選項將NSData對象寫入文件,options參數是指定數據選項,error參數是返回讀取數據的錯誤。

將數組數據寫入plist文件

BOOL isSuccess_2 =  [infos writeToFile:filePath_array atomically:YES];
  
    if (isSuccess_2) {
        NSLog(@"數據寫入成功");
    } else {        
        NSLog(@"寫入失敗");
    }

NSDictionary方法同理。

##2.1 擴展Preference 在程序啓動後,系統會自動建立一個NSUserDefaults的單例對象,咱們能夠獲取這個單例來存儲少許的數據,它會將輸出存儲在.plist格式的文件中。其優勢是像字典同樣的賦值方式方便簡單,但缺點是沒法存儲自定義的數據。

ps:每次操做後都要數據同步。

得到NSUserDefaults系統提供的plist文件:

NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];

設值:

[userDefaults setObject:@100 forKey:@"id"];
    [userDefaults setBool:YES forKey:@"isLoad"];
    [userDefaults setDouble:3.8408 forKey:@"price"];

數據同步(即便更新數據):

[userDefaults synchronize];

取值:

id value = [userDefaults objectForKey:@"id"];
    NSLog(@"%@",value);

修改:

[userDefaults setObject:@2000 forKey:@"id"];
    [userDefaults synchronize];

##三、對象歸檔

與屬性列表相反,一樣做爲輕量級存儲的持久化方案,數據歸檔是進行加密處理的,數據在通過歸檔處理會轉換成二進制數據,因此安全性要遠遠高於屬性列表。另外使用歸檔方式,咱們能夠將複雜的對象寫入文件中,而且無論添加多少對象,將對象寫入磁盤的方式都是同樣的。

對一個對象進行完整歸檔須要知足的條件爲:該對象的類必須實現NSCoding協議,並且每一個成員變量都應該是基本數據類型或都是實現NSCoding協議的某個類的實例。

@interface User : NSObject <NSCoding>

@property (nonatomic, copy) NSString *userName;
@property (nonatomic, copy) NSString *password;

歸檔類NSKeyedArchiver和反歸檔類NSKeyedUnarchiver總與NSData關聯在一塊兒。NSData封裝了字節數據的緩存類,提供類讀寫數據的方法,具體以下所示:

dataWithContentsOfFile:他是靜態工廠方法,用於從文件讀取數據來建立NSData對象,

dataWithContentsOfFile:options:error:它是靜態工廠方法,用於從文件讀取數據來建立NSData對象,options參數是指定讀取數據選項,error參數是返回讀取數據的錯誤。

initWithContentsOfFile:options:error:它是實例構造函數,同上一個方法同樣。

writeToFile:options:error:將NSData對象寫入文件,atomically參數是否寫入輔助文件,NO時數據直接寫入目標文件路徑,ture時數據寫入輔助文件,寫入成功後將輔助文件路徑更名爲目標文件路徑。當目標文件已經存在時,atomically設置爲false,能夠防止系統崩潰所致使舊文件的破壞。

歸檔(編碼保存):將數據以二進制的形式保存,經常使用的NSString、NSArray、NSDictionary.... 能夠直接歸檔保存,由於它們都實現了 <NSCoding> 協議 : NSKeyedArchiver 歸檔代碼:

BOOL isSuccess = [NSKeyedArchiver archiveRootObject:user toFile:filePath];

歸檔過程是使用NSKeyedArchiver對象歸檔數據,具體過程爲:首先將歸檔數據寫入NSData對象,最後再將NSData對象寫入歸檔文件,反歸檔過程是從歸檔文件中讀取NSData對象,再利用NSKeyedUnarchiver對象從NSData對象中反歸檔數據。

歸檔協議方法實現:

#pragma mark - <NSCoing> 對屬性編碼和解碼
// 編碼
- (void)encodeWithCoder:(NSCoder *)aCoder {
    
    [aCoder encodeObject:_userName forKey:@"userName"];
    [aCoder encodeObject:_password forKey:@"password"];
}


// 解碼
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder {
    
    self = [super init];
    if (self ) {       
        self.userName = [aDecoder decodeObjectForKey:@"userName"];
        self.password = [aDecoder decodeObjectForKey:@"password"];
    }
    
    return self;
}

解歸檔(讀取解碼): NSKeyedUnarchiver:

id object = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
    User *user_ = (User *)object;

##四、SQLite數據庫

結構化查詢語⾔(Structured Query Language)簡稱SQL,是⼀一種特殊⺫的的編程語言,是一種數據庫查詢和程序設計語言,⽤用於存取數據以及查詢、更新和管理關係數據庫系統。

SQLite的數據類型:

  1. NULL,值是NULL
  2. INTEGER,值是有符號整形,根據值的大⼩以1,2,3,4,6或8字節存放
  3. REAL,值是浮點型值,以8字節IEEE浮點數存放
  4. TEXT,值是文本字符串,使用數據庫編碼(UTF-8,UTF-16BE或者UTF-16LE)存放 5 BLOB,只是一個數據塊,徹底按照輸入存放(即沒有準換)

SQLite提供是C語言的庫,使用的時候調用的API都是函數,這些常⽤用的函數包括數據庫的建立打開、語句的預處理、執行、關閉等。

sqlite3: 數據庫結構體,至關於數據庫類。

sqlite3_stmt: sql語句結構體,至關於SQL語句類。

sqlite3_open(): 建立和打開數據庫,返回值等於 SQLITE_OK 表⽰示成功。

sqlite3_exec(): 執⾏非查詢sql語句,返回值等於 SQLITE_OK 表示執⾏成功。 sqlite3_prepare_v2(): 預處理sql語句,用於數據查詢。

sqlite3_step(): 能夠⽤於屢次執行sql語句,要先使⽤預處理,用於數據查詢。 sqlite3_bind_text(): 綁定sql語句中 ?號對應的參數。

sqlite3_column_text(): 從sqlite3_step()的結果中獲取指定列的數據,包含 int,double等多個。

sqlite3_finalize():銷燬前⾯面被sqlite3_prepare()建立的預處理語句sqlite3_stmt,在關閉以前完成。

sqlite3_close():關閉以前打開的數據庫,使用完後必須關閉數據庫釋放資源。

###4.1 導入SQLite系統庫

OS中使用SQLite得依賴 libsqlite3.dylib 庫,因此使用前先要導入這個庫。步驟以下:

點擊項目文件 -> TARGETS -> Build Phases -> Link Binary With Librarise -> 點擊 + 號,XCode7之後不能直接搜索到libsqlite3.dylib,只能搜到libsqlite3.tbd,須要到指定目錄中添加,因此須要以下圖操做:

一、點擊Add Other

二、快捷鍵 command + shift + G 前往文件夾,輸入/user/lib/libsqlite3.dylib 到達庫文件目錄。

三、選擇libsqlite3.dylib ,OK了。

###4.2 數據庫的操做

1.建立數據庫,數據庫擴展名稱爲.sqlite,sqlite3_open()函數包含兩個參數,第一個爲數據庫的路徑由於參數的類型是 char *類型,全部要用[dataPath UTF8String]進行類型轉換,第二個爲數據庫sqlite3指針的地址。

// 一、建立數據庫指針,至關於建立一個數據庫對象。
	sqlite3 *_sqlite;

//建立數據庫的保存路徑
    NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    
    NSString *sqlPath = [docPath stringByAppendingPathComponent:@"dbName.sqlite"];
    
    int result = sqlite3_open([sqlPath UTF8String], &_sqlite);
    //若是函數的返回值等於 SQLITE_OK(宏) 表示成功
    if (result == SQLITE_OK) {
        NSLog(@"數據庫建立成功,%@",docPath);
}
	//數據庫打開失敗或者使用完後都要調用sqlite3_close()函數,關閉數據庫釋放資源。
    sqlite3_close(_sqlite);

2.建立表,sqlite3_exec()執行sql語句函數,包含五個參數,第一個是數據庫指針,第二個sql語句(char 類型),第三個回調函數,若是不須要回調就用NULL。第四個回調函數的的第一個參數沒有用NULL,第五個錯誤信息地址 char *類型。

NSString *sql = @"CREATE TABLE IF NOT EXISTS User('userID' TEXT, 'userName' TEXT, 'userAge' INTEGER)";
    
    // sqlite3_exec() 執行 sql語句 函數
    int result = sqlite3_exec(_sqlite,[sql UTF8String],NULL,NULL,NULL);
    
    if (result == SQLITE_OK) {
        
        NSLog(@"建立表成功");
    }

3.添加數據

NSString *userID = @"100012";
    NSString *userName = @"加大對";
    int age = 20;
    
    NSString *sql = [NSString stringWithFormat:@"INSERT INTO User('userID','userName','userAge') VALUES('%@','%@',%d)",userID,userName,age];
    
    // sqlite3_exec() 執行 sql 函數
    int result = sqlite3_exec(_sqlite,[sql UTF8String],NULL,NULL,NULL);
    
    if (result == SQLITE_OK) {
        
        NSLog(@"插入數據成功");
    }

4.添加字段

NSString *columnName = @"userSex TEXT";
    NSString *sql = [NSString stringWithFormat:@"ALTER TABLE User ADD COLUMN %@",columnName];
    
    // sqlite3_exec() 執行 sql 函數
    int result = sqlite3_exec(_sqlite,[sql UTF8String],NULL,NULL,NULL);
    
    if (result == SQLITE_OK) {
        
        NSLog(@"添加字段成功");
    }

5.更新數據

NSString *userSex = @"男";
    NSString *userName = @"電風扇";
    NSString *sql = [NSString stringWithFormat:@"UPDATE User SET userSex = '%@' WHERE userName = '%@'",userSex,userName];
    
    // sqlite3_exec() 執行 sql 函數
    int result = sqlite3_exec(_sqlite,[sql UTF8String],NULL,NULL,NULL);
    
    if (result == SQLITE_OK) {
        
        NSLog(@"修改數據成功成功");
    } else {
        
        NSLog(@"失敗");
    }

6.刪除數據

NSString *userName = @"電風扇";
    NSString *sql = [NSString stringWithFormat:@"DELETE FROM User WHERE userName = '%@'",userName];
    
    // sqlite3_exec() 執行 sql 函數
    int result = sqlite3_exec(_sqlite,[sql UTF8String],NULL,NULL,NULL);
    
    if (result == SQLITE_OK) {
        
        NSLog(@"刪除數據成功");
        
    } else {
        
        NSLog(@"失敗");
    }

7.查詢數據

//一、建立sql語句結構體指針(能夠理解爲sql語句對象)
    sqlite3_stmt *_stmt;
    
    // NSString *sql = @"SELECT *FROM User";
    
    NSString *key = @"張";
    NSString *sql = [NSString stringWithFormat:@"SELECT *FROM User WHERE userName LIKE '%%%@%%'",key];
    
    
    // 二、編譯查詢語句
    int result = sqlite3_prepare_v2(_sqlite,[sql UTF8String],-1,&_stmt,NULL);
    
    if (result == SQLITE_OK) {
        NSLog(@"sql語句編程經過");
        
        NSMutableArray *users = [NSMutableArray array];
        
        // 三、執行查詢語句,== SQLITE_ROW 表示還有下一條數據
        while (sqlite3_step(_stmt) == SQLITE_ROW) {
            
            /* 四、獲取每一行數據對應的字段(列)
             
             sqlite3_column_text() 對應 TEXT 類型
             sqlite3_column_int() 對應 INTERER類型
             */
            char *userID = (char *)sqlite3_column_text(_stmt,0);
            char *userName = (char *)sqlite3_column_text(_stmt,1);
            int userAge = sqlite3_column_int(_stmt,2);
            char *userSex = (char *)sqlite3_column_text(_stmt,3);
            
            NSString *userID_str = [NSString stringWithUTF8String:userID];
            NSString *userName_str = [NSString stringWithUTF8String:userName];
            NSString *userSex_str = [NSString stringWithUTF8String:userSex];
            
            // 把查詢到的數據保存爲 model 對象
            User *user = [[User alloc] init];
            user.userID = userID_str;
            user.userName = userName_str;
            user.userSex = userSex_str;
            user.userAge = userAge;
            [users addObject:user];
            
            NSLog(@"%@, %@, %d,%@",userID_str,userName_str,userAge,userSex_str);
            
        }
        
    }

###4.3 第三方框架(FMDB)使用

iOS開發時,項目中會引用許多第三方庫,CocoaPods能夠用來方便的統一管理這些第三方庫,安裝方法.

相關文章
相關標籤/搜索