數據保存(永久保存)方式

1、數據保存(永久保存)方式有五種:html

 

1.NSUserDefaults:保存設置數據算法

2.歸檔:保存自定義數據sql

3.文件(plist,txt)數據庫

4.數據庫和CoreData數組

5.KeyChain(鑰匙串—系統中 鑰匙串訪問 這個程序)安全

 

只有數據庫和CoreData才適合用於保存大量的數據(效率高,由於有數據庫的算法),其它方式只用於保存少許數據(保存大量數據效率低)。多線程

前四個在沙盒範圍內的,第五個即便刪除app也存在於手機中。app

KeyChain鑰匙串是單向加密的,最爲安全。異步

 

數據保存能夠提高app的體驗度:一開始進入app,顯示的是上次關閉時的數據;當刷新時才請求新的數據atom

數據保存多數用於實現`收藏`功能

 

2、NSUserDefaults   

(1)只能保存基本數據類型(int,float,double,BOOL,NSString,NSArray,NSDictionary,NSData,NSURL等),不能保存自定義的對象

(2)用於保存系統的設置型數據,如手機操做系統裏面的`通用`設置數據

(3)NSUserDefaults 系統採起按期保存的機制,具體多久保存一次未知,也可手動採起當即保存(強制保存)(須要調用synchronize)

(4)只用於保存少許數據

(5)以鍵值對的形式保存

  基本操做:

    //set方法保存(按期保存,若是當即保存,須要調用synchronize)
    [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"key1"];
    [[NSUserDefaults standardUserDefaults] setObject:@[] forKey:@"key2"];//數組

    //強制保存
    [[NSUserDefaults standardUserDefaults] synchronize];
    
    //取值
    [[NSUserDefaults standardUserDefaults] boolForKey:@"key"];
    
    //刪除
    [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"key"];

  應用例子:自動登陸

#import "ViewController.h"

//自動登陸
//#define kAutoLoginKey @"kAutoLoginKey"

@interface ViewController ()
@property (weak, nonatomic) IBOutlet UITextField *username;
@property (weak, nonatomic) IBOutlet UITextField *password;
- (IBAction)login:(id)sender;
- (IBAction)autoLoginBtnClick:(UIButton *)sender;
@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    //自動登陸
    if ([[NSUserDefaults standardUserDefaults] boolForKey:@"kAutoLoginKey"])
    {
        //發送登陸請求,而後請求主頁界面的數據
        NSLog(@"發送登陸請求");
    }
    else
    {
        //彈出登陸界面,登陸成功後dismiss掉登陸界面
        NSLog(@"彈出登陸");
    }
}
- (IBAction)login:(id)sender {
    //若勾選了自動登陸按鈕,保存用戶信息到 NSUserDefaults
}
//自動登陸按鈕
- (IBAction)autoLoginBtnClick:(UIButton *)sender
{
    //修改按鈕狀態
    sender.selected = !sender.selected;   
    //保存當前狀態
    [[NSUserDefaults standardUserDefaults] setBool:sender.selected forKey:@"kAutoLoginKey"];
    [[NSUserDefaults standardUserDefaults] synchronize];
}
@end

    

   拓展應用:重啓app,記錄上次的選中狀態。

3、歸檔(序列化)與解歸檔(反序列化)

(1)能保存自定義的對象,解決NSUserDefaults不能解決的問題

(2)轉化成NSData,以鍵值對的形式保存:要保存的對象(類),被轉化爲NSData,保存到NSUserDefaults下

(3)歸檔和反歸檔須要`被歸檔的類`遵照NSCoding協議,自定義的類須要重寫`encodeWithCoder:`和`initWithCoder:`方法

 

#import <Foundation/Foundation.h>
//歸檔須要遵照NSCoding協議
@interface Person : NSObject<NSCoding>
@property (nonatomic, copy) NSString *name;
@property (nonatomic) NSInteger age;
+ (instancetype)personWithName:(NSString *)name age:(int)age;
@end

#import "Person.h"
@implementation Person
+ (instancetype)personWithName:(NSString *)name age:(int)age
{
    Person *person = [[self alloc] init];
    person.name = name;
    person.age = age;
    return person;
}
//歸檔的時候會調用,也就是調用encodeObject:forKey:
- (void)encodeWithCoder:(NSCoder *)aCoder
{
    //保存
    [aCoder encodeObject:self.name forKey:@"name"];
    [aCoder encodeObject:@(self.age) forKey:@"age"];
    //encodeObject:(id類型),所以int類型沒法保存
}
//解歸檔的時候會調用,也就是調用decodeObjectForKey:
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder
{
    if (self = [super init])
    {
        //取值
        self.name = [aDecoder decodeObjectForKey:@"name"];
        self.age = [[aDecoder decodeObjectForKey:@"age"] integerValue];
    }
    return self;
}
@end

#import "ViewController.h"
#import "Person.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad { [super viewDidLoad]; [self encode]; [self decode]; } /** * 歸檔 */ - (void)encode { Person *p1= [Person personWithName:@"小明" age:20]; Person *p2 = [Person personWithName:@"小張" age:30]; NSArray *array = @[p1,p2]; //保存對象轉化爲二進制數據(必定是可變對象) NSMutableData *data = [NSMutableData data]; //1.初始化 NSKeyedArchiver *archivier = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data]; //2.歸檔 [archivier encodeObject:array forKey:@"key"]; //3.完成歸檔 --- 必須調用,不然報警告,且沒法完成歸檔 [archivier finishEncoding]; //4.保存 [[NSUserDefaults standardUserDefaults] setObject:data forKey:@"data"]; //保存到指定目錄: //[data writeToFile:保存路徑 atomically:YES]; /* atomically: 爲YES,表示先寫入到臨時目錄,確認整個文件保存過程當中沒出錯後,再自動寫入到指定的目錄 爲NO,表示直接寫入到指定的目錄,就算出錯也繼續寫入 所以推薦 atomically:YES */
NSLog(@"%@",data); } /** * 解歸檔 */ - (void)decode { //獲取保存的數據 NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:@"data"]; //1.初始化 NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data]; //2.解歸檔 NSArray *persons = [unarchiver decodeObjectForKey:@"key"]; //3.完成解歸檔 [unarchiver finishDecoding]; for (Person *person in persons) { NSLog(@"%@ - %ld",person.name,(long)person.age); } } @end

  `

4、文件保存  writeToFile:

一、plist 文件

(1)建立方式:直接建立、代碼建立:把數據保存到plist文件中

(2)plist 文件 屬於 xml 文件

二、txt 文件

NSString *cachePath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0];
#if 0
    BOOL isOK = [@"111" writeToFile:[cachePath stringByAppendingPathComponent:@"1.txt"] atomically:YES encoding:NSUTF8StringEncoding error:nil];
#endif
    NSArray *array = @[@"111",@"222",@{@"key1":@"value1",@"key2":@[@"333",@"444"]}];
    
    BOOL isOK = [array writeToFile:[cachePath stringByAppendingPathComponent:@"1.plist"] atomically:YES];
    
    if (!isOK)
    {
        NSLog(@"寫入失敗");
    }
    else
    {
        NSLog(@"寫入成功");
    }

 

5、數據庫和CoreData

 

一、基本sql語句(稍後另做隨筆添加)

 

二、fmdb 第三方庫

(1)線程不安全的

  (直接調用 FMDatabase 對象,執行讀寫等操做)

    //獲取Document路徑
    NSURL *url = [[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask][0];
    //把url對象轉換成string
    NSLog(@"%@",[url absoluteString]);//本地路徑
    NSLog(@"%@",[url relativePath]);//相對路徑
    //設置建立數據庫的路徑(包行數據庫文件的命名)
    NSString *path = [[url relativePath] stringByAppendingPathComponent:@"user.db"];

/****************  線程不安全  ****************/
    //初始化database對象
    FMDatabase *database = [FMDatabase databaseWithPath:path];
    
    //建立並打開/打開數據庫:若是數據庫不存在,就建立而且打開;不然直接打開。
    [database open];
    
    //除了查詢(select)是query操做以外,其餘的都是update操做
//    [database executeUpdate:<#(NSString *), ...#>];
//    [database executeQuery:<#(NSString *), ...#>];
    
    //關閉數據庫
    [database close];

 

(2)線程安全的

  (調用 FMDatabaseQueue 對象來執行數據庫操做)

  咱們知道直接使用libsqlite3進行數據庫操做實際上是線程不安全的,若是遇到多個線程同時操做一個表的時候可能會發生意想不到的結果。爲了解決這個問題建議在多線程中使用FMDatabaseQueue對象,相比FMDatabase而言,它是線程安全的。

  保證每一次讀寫等操做都不是異步的,而是同步的;同時有兩個線程進入該操做時,按順序執行。

/****************  線程安全  ****************/
    FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:path];
    //執行sql語句 (線程安全的數據庫不須要打開,inDatabase內含打開數據庫的代碼)
    [queue inDatabase:^(FMDatabase *db) {
        //執行增刪改查
        
        NSString *sql = @"select * from xx";
        
        [db executeQuery:sql];
        [db executeUpdate:@""];
    }];

   注意:dataWithPath中的路徑參數通常會選擇保存到沙箱中的Documents目錄中;若是這個參數設置爲nil則數據庫會在內存中建立;若是設置爲@」」則會在沙箱中的臨時目錄建立,應用程序關閉則文件刪除。

(3)兩個基本方法

  FMDatabase類提供了兩個方法`executeUpdate:`和`executeQuery:`分別用於執行無返回結果的查詢和有返回結果的查詢。須要指出的是,若是調用有格式化參數的sql語句時,格式化符號使用「?」而不是「%@」、等。下面是兩種狀況的代碼片斷:

a.無返回結果

-(void)executeNonQuery:(NSString *)sql{
    //執行更新sql語句,用於插入、修改、刪除
    if (![self.database executeUpdate:sql]) {
        NSLog(@"執行SQL語句過程當中發生錯誤!");
    }
}

 

b.有返回結果

-(NSArray *)executeQuery:(NSString *)sql{
    NSMutableArray *array=[NSMutableArray array];
    //執行查詢sql語句
    FMResultSet *result= [self.database executeQuery:sql];
    while (result.next) {
        NSMutableDictionary *dic=[NSMutableDictionary dictionary];
        for (int i=0; i<result.columnCount; ++i) {
            dic[[result columnNameForIndex:i]]=[result stringForColumnIndex:i];
        }
        [array addObject:dic];
    }
    return array;
}

  對於有返回結果的查詢而言,查詢完返回一個遊標FMResultSet,經過遍歷遊標進行查詢。並且FMDB中提供了大量intForColumn、stringForColumn等方法進行取值。

(3)事務

  用於多線程多個同時操做時,保證操做全都成功,不然全不成功。

  不是隻有FMDB才支持事務,sql自己就支持事務,FMDB將其封裝成了幾個方法來調用,不用本身寫對應的sql。其實在在使用libsqlite3操做數據庫時也是原生支持事務的(由於這裏的事務是基於數據庫的,FMDB仍是使用的SQLite數據庫),只要在執行sql語句前加上「begin transaction;」執行完以後執行「commit transaction;」或者「rollback transaction;」進行提交或回滾便可。另外在Core Data中你們也能夠發現,全部的增、刪、改操做以後必須調用上下文的保存方法,其實自己就提供了事務的支持,只要不調用保存方法,以前全部的操做是不會提交的。

  在FMDB中FMDatabase有beginTransaction、commit、rollback三個方法進行開啓事務、提交事務和回滾事務。

    //事務:要麼操做都成功,要麼都不成功。
    FMDatabase *database = [FMDatabase databaseWithPath:path];
    
    //打開數據庫:若是數據庫不存在,就建立而且打開。不然直接打開。
    [database open];
    
    //建立表
    NSString *sql = @"create table if not exists User (id integer primary key autoincrement, name text)";
    //執行sql
    [database executeUpdate:sql];
    
    sql = @"insert into User (name) values (?)";
    
    //添加事務
    [database beginTransaction];
    
    //插入10條數據
    for (int i = 0; i < 10; i++)
    {
        BOOL isOK = [database executeUpdate:sql,[NSString stringWithFormat:@"test%d",i+1]];
        
        //在這裏模擬失敗
        if (i == 5)
        {
            //操做失敗,回滾數據庫:取消本次操做,把數據庫回滾到`添加事務前`的狀態
            [database rollback];
            return;
        }
        
        if (isOK)
        {
            NSLog(@"插入成功");
        }
        else
        {
            NSLog(@"插入失敗");
            //正常的程序設計,都會在插入失敗這裏設置回滾
            [database rollback];
            return;
        }
    }
    
    NSLog(@"%@",path);
    
    //提交事務
    [database commit];
    
    //運行結果:插入數據成功,但數據庫中沒有數據 --- 事務。

  事務模擬轉帳:

    //添加事務
    [database beginTransaction];
//轉帳 BOOL isOK = [database executeUpdate:sql,[NSString stringWithFormat:@"test%d",i+1]]; if (!isOK) { [database rollback]; return; }
//收帳 isOK = [database executeUpdate:sql,[NSString stringWithFormat:@"test%d",i+1]]; if (!isOK) { [database rollback]; return; } [database commit];

 

(4)fmdb的二次封裝

 

三、CoreData

(1)基本操做

(2)實體和實體之間的關係

(3)版本升級和數據遷移

6、KeyChain

 

()以鍵值對的形式保存數據

相關文章
相關標籤/搜索