iOS 數(shù)據(jù)持久化

所謂的持久化,就是將數(shù)據(jù)保存到硬盤中,使得在應(yīng)用程序或機(jī)器重啟后可以繼續(xù)訪問(wèn)之前保存的數(shù)據(jù)。在iOS開(kāi)發(fā)中,有很多數(shù)據(jù)持久化的方案.
在開(kāi)發(fā)中常用的存儲(chǔ)方案主要有以下幾種:
1 plist文件(屬性列表)
2.NSKeyedArchiver(歸檔)
3.SQLite 3
4.CoreData(這個(gè)不經(jīng)常用)
5.keyChain

沙盒
在介紹各種存儲(chǔ)方法之前,有必要說(shuō)明以下沙盒機(jī)制。iOS程序默認(rèn)情況下只能訪問(wèn)程序自己的目錄,這個(gè)目錄被稱為“沙盒”。
沙盒的目錄結(jié)構(gòu)如下圖:


ss.png

Documents文件夾。存儲(chǔ)用戶數(shù)據(jù)和用戶文檔。
Library文件夾。存儲(chǔ)系統(tǒng)數(shù)據(jù)。
tmp文件夾。臨時(shí)文件夾,用于存儲(chǔ)臨時(shí)數(shù)據(jù),這個(gè)文件夾不穩(wěn)定,數(shù)據(jù)可能被清除。
SystemData 最近剛加的,存儲(chǔ)系統(tǒng)數(shù)據(jù)

1.1存儲(chǔ)為plist文件
plist文件是將某些特定的類,通過(guò)XML文件的方式保存在目錄中。

可以被序列化的類型只有如下幾種:必須要遵循
NSArray;
NSMutableArray;
NSDictionary;
NSMutableDictionary;
NSData;
NSMutableData;
NSString;
NSMutableString;
NSNumber;
NSDate;

eg:存入字符串

NSString *path = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/myFile.plist"];
    
    NSError *err = nil;
    
    //writeToFile把字符串存入文件。第一個(gè)參數(shù)表示存儲(chǔ)的路徑(包含文件名),第二個(gè)參數(shù)表示是否原子性寫入(如果多個(gè)線程可能同時(shí)操作這個(gè)文件,原子性需要寫YES),第三個(gè)參數(shù)表示文本的編碼方式(字符串存儲(chǔ)到硬盤上必須先轉(zhuǎn)換為二進(jìn)制數(shù)據(jù),選擇按照哪種編碼格式進(jìn)行編碼)。第四個(gè)參數(shù)表示如果存儲(chǔ)失敗,失敗的原因。
    if (![@"天涯明月新,朝暮最相思" writeToFile:path atomically:NO encoding:NSUTF8StringEncoding error:&err]) {
        NSLog(@"%@",err);
    }

讀取字符串

 NSString *path = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/myFile.plist"];
    //從文件中讀取字符串,第一個(gè)參數(shù),讀取的文件的路徑,第二個(gè)參數(shù)編碼方式,第三個(gè)參數(shù),如果失敗,失敗的原因。
    NSString *text = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
    NSLog(@"%@",text);

eg:存入數(shù)組

   NSArray *array = @[@"惟將長(zhǎng)夜未開(kāi)眼",@"報(bào)答平生未展眉"];
    
    NSString *path = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/array.plist"];
    
    //把數(shù)組存入文件
    [array writeToFile:path atomically:NO];

讀取數(shù)組

NSString *path = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/array.plist"];
    //從文件中讀取數(shù)組
    NSArray *array = [NSArray arrayWithContentsOfFile:path];
    for (NSString *strs in array)
    {
            NSLog(@"%@",strs);
    }

1.2 NSUserDefaults 適用于輕量級(jí)數(shù)據(jù)了比較小的,一般存儲(chǔ)用戶信息的相關(guān)數(shù)據(jù)。

//1.獲得NSUserDefaults文件
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
//2.向文件中寫入內(nèi)容
[userDefaults setObject:@"AAA" forKey:@"a"];
[userDefaults setBool:YES forKey:@"sex"];
[userDefaults setInteger:21 forKey:@"age"];
//2.1立即同步
[userDefaults synchronize];
//3.讀取文件
NSString *name = [userDefaults objectForKey:@"a"];
BOOL sex = [userDefaults boolForKey:@"sex"];
NSInteger age = [userDefaults integerForKey:@"age"];
NSLog(@"%@, %d, %ld", name, sex, age);
  1. NSKeyedArchiver 數(shù)據(jù)序列化,只有遵守了NSCoding或 NSSecureCoding(更為安全的歸檔協(xié)議)協(xié)議,并且實(shí)現(xiàn)了協(xié)議里歸檔與解歸檔的方法的的類創(chuàng)建的對(duì)象才能夠進(jìn)行歸檔
    一般是對(duì)自己創(chuàng)建的模型進(jìn)行存儲(chǔ)是會(huì)用到,
    需要主要以下三點(diǎn):
    1.必須遵循并實(shí)現(xiàn)NSCoding協(xié)議
    2.保存文件的擴(kuò)展名可以任意指定
    3.繼承時(shí)必須先調(diào)用父類的歸檔解檔方法
    eg:建一個(gè)Person類
    .h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface Person : NSObject
@property (strong, nonatomic)UIImage *avatar;
@property (copy, nonatomic) NSString *name;
@property (assign, nonatomic) NSInteger age;
@end

.m

#import "Person.h"

@implementation Person

//解檔
- (id)initWithCoder:(NSCoder *)aDecoder
{
    if ([super init])
    {
        self.avatar = [aDecoder decodeObjectForKey:@"avatar"];
        self.name = [aDecoder decodeObjectForKey:@"name"];
        self.age = [aDecoder decodeIntegerForKey:@"age"];
    }
    return self;
}
//歸檔
- (void)encodeWithCoder:(NSCoder *)aCoder
{
    [aCoder encodeObject:self.avatar forKey:@"avatar"];
    [aCoder encodeObject:self.name forKey:@"name"];
    [aCoder encodeInteger:self.age forKey:@"age"];
}

@end

存入person:


  NSString *path = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/person.plist"];

    Person *person = [[Person alloc] init];
    person.avatar = [UIImage imageNamed:@"ss"];
    person.name = @"小白";
    person.age = 22;
    [NSKeyedArchiver archiveRootObject:person toFile:path];

讀取person:

 NSString *path = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/person.plist"];
    Person *person = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
    if (person)
    {
        NSLog(@"名字是:%@",person.name);
        NSLog(@"年齡是:%ld",person.age);
    }

3.FMDB
大量數(shù)據(jù)的存儲(chǔ)我們一般用FMDB,比如聊天中的信息一般是用FMDB存儲(chǔ)的,F(xiàn)MDB是在sqlite的基礎(chǔ)上進(jìn)行封裝的.
一般我們?cè)谑褂脮r(shí)會(huì)對(duì)FMDB進(jìn)行再次封裝,我們對(duì)數(shù)據(jù)的操作一般也就是增,刪,改,查。將fmdb 封裝成一個(gè)工具類。將需要存儲(chǔ)的信息封裝成一個(gè)model。代碼如下:
PersonInfoModel.h

#import <Foundation/Foundation.h>

@interface PersonInfoModel : NSObject
@property (nonatomic,strong)NSString *userName;
@property (nonatomic,strong)NSString *headImage;
@property (nonatomic,strong)NSString *nickname;
@property (nonatomic,assign)NSInteger haveMoney;
@end

FmdbTool.h

#import <Foundation/Foundation.h>
@class FMDatabase;
@class PersonInfoModel;
@interface FmdbTool : NSObject
@property (nonatomic, strong) FMDatabase *db;


+ (instancetype)shareManager;

//查找
- (PersonInfoModel *)selectInfoWithUserName:(NSString *)username;

//增加
- (void)saveInfoWithPersonalModel:(PersonInfoModel *)model;

//刪除
- (void)deleteInfoWithUserName:(NSString *)username;

//改
- (void)updateInfoWithPersonModel:(PersonInfoModel *)model;
@end

FmdbTool.m

#import "FmdbTool.h"
#import "FMDB.h"
#import "PersonInfoModel.h"
static FmdbTool  *__help = nil;
@implementation FmdbTool
+ (instancetype)shareManager
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        __help = [[FmdbTool alloc] init];
        [__help createDataBase];
        [__help createTableView];
    });
    
    return __help;
}
- (void)createDataBase
{
        NSString *path = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/conversionInfo.sqlite"];
        _db = [[FMDatabase alloc] initWithPath:path];
        [_db open];
        [_db close];
}
-(void)createTableView
{
    NSString *sql = @"CREATE TABLE IF NOT EXISTS ConversionInfo(conversionInfo_id INTEGER PRIMARY KEY AUTOINCREMENT,userName TEXT,headImage TEXT,nickname TEXT,haveMoney INTEGER)";
    if ([self.db open])
    {
        if ([_db executeUpdate:sql])
        {
            NSLog(@"創(chuàng)建表成功");
        }
        else
        {
            NSLog(@"創(chuàng)建表失敗");
        }
         [self.db close];
    }
    else
    {
        NSLog(@"打開(kāi)數(shù)據(jù)庫(kù)失敗");
    }
   
}
- (PersonInfoModel *)selectInfoWithUserName:(NSString *)username
{
    [_db open];
    NSString *sql = [NSString stringWithFormat:@"SELECT * FROM ConversionInfo where userName='%@'",username];
    FMResultSet *set =  [_db executeQuery:sql];
    PersonInfoModel *model = [[PersonInfoModel alloc] init];
    while ([set next])
    {
//        int studentID = [set intForColumn:@"student_id"];
        
        NSString *name = [set stringForColumn:@"userName"];
        NSString *headImage = [set stringForColumn:@"headImage"];
        NSString *nickname = [set stringForColumn:@"nickname"];
        int haveMoney = [set intForColumn:@"haveMoney"];
       
        model.userName = name;
        model.headImage = headImage;
        model.nickname = nickname;
        model.haveMoney = haveMoney;
     

//        NSDictionary *dictionary =  [set resultDictionary];
//        NSLog(@"%d,%@,%d,%@",studentID,name,age,dictionary);
    }
    
   
    [set close];
    [_db close];
    
     return model;
}

- (void)saveInfoWithPersonalModel:(PersonInfoModel *)model
{
     [_db open];
    NSString *sql = [NSString stringWithFormat:@"INSERT INTO ConversionInfo(userName, headImage, nickname,haveMoney) VALUES('%@','%@','%@',%ld)",model.userName,model.headImage,model.nickname,(long)model.haveMoney];
//     NSString *sql = @"insert into app (_id,updated,cover,title) values (?,?,?,?)"
    BOOL result = [_db executeUpdate:sql];
//    BOOL result = [_db executeUpdate:@"%@",sql];
    if (result)
    {
        NSLog(@"插入成功");
    }
    else
    {
        NSLog(@"插入失敗");
    }
    
    [_db close];
    
}
- (void)deleteInfoWithUserName:(NSString *)username
{
    [_db open];
   NSString *sql = [NSString stringWithFormat:@"DELETE FROM ConversionInfo where userName='%@'",username];
   BOOL result = [_db executeUpdate:sql];
    if (result)
    {
        NSLog(@"刪除成功");
    }
    else
    {
        NSLog(@"刪除失敗");
    }
    [_db close];
}
- (void)updateInfoWithPersonModel:(PersonInfoModel *)model
{
    [_db open];
    NSString *sql = [NSString stringWithFormat:@"update ConversionInfo set headImage='%@',nickname='%@',haveMoney=%ld where userName='%@'",model.headImage,model.nickname,(long)model.haveMoney,model.userName];
    BOOL result = [_db executeUpdate:sql];
    if (result)
    {
        NSLog(@"修改成功");
    }
    else
    {
        NSLog(@"修改失敗");
    }
    [_db close];
}
@end
  1. CoreData 這個(gè)基本很少用

5.KeyChain
iOS keychain 是一個(gè)相對(duì)獨(dú)立的空間,保存到keychain鑰匙串中的信息不會(huì)因?yàn)樾遁d/重裝app而丟失, 。相對(duì)于NSUserDefaults、plist文件保存等一般方式,keychain保存更為安全。所以我們會(huì)用keyChain保存一些私密信息,比如密碼、證書、設(shè)備唯一碼(把獲取到用戶設(shè)備的唯一ID 存到keychain 里面這樣卸載或重裝之后還可以獲取到id,保證了一個(gè)設(shè)備一個(gè)ID)等等。keychain是用SQLite進(jìn)行存儲(chǔ)的。用蘋果的話來(lái)說(shuō)是一個(gè)專業(yè)的數(shù)據(jù)庫(kù),加密我們保存的數(shù)據(jù),可以通過(guò)metadata(attributes)進(jìn)行高效的搜索。keychain適合保存一些比較小的數(shù)據(jù)量的數(shù)據(jù),如果要保存大的數(shù)據(jù),可以考慮文件的形式存儲(chǔ)在磁盤上,在keychain里面保存解密這個(gè)文件的密鑰。
如果需要在應(yīng)用里使用使用keyChain,我們需要導(dǎo)入Security.framework ,keychain的操作接口聲明在頭文件SecItem.h里。但是自己需要自己封裝有點(diǎn)復(fù)雜。我這邊使用的是第三方封裝好的SSKeychain。導(dǎo)入即可使用
保存密碼:

 if ( [SAMKeychain setPassword:@"白首如新,傾蓋如故" forService:@"test" account:@"testAccount"])
    {
        NSLog(@"keyChin存儲(chǔ)成功");
    }
    else
    {
        NSLog(@"keyChain存儲(chǔ)失敗");
    }

讀取密碼

NSString *passWord = [SAMKeychain passwordForService:@"test" account:@"testAccount"];
    NSLog(@"passWord is %@",passWord);

Demo地址

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1.簡(jiǎn)介 數(shù)據(jù)持久存儲(chǔ)是一種非易失性存儲(chǔ),在重啟動(dòng)計(jì)算機(jī)或設(shè)備后也不會(huì)丟失數(shù)據(jù)。持久化技術(shù)主要用于MVC模型中的m...
    公子無(wú)禮閱讀 1,773評(píng)論 0 4
  • Title: iOS數(shù)據(jù)持久化 ##數(shù)據(jù)持久化概念 數(shù)據(jù)持久化就是將內(nèi)存中的數(shù)據(jù)模型轉(zhuǎn)換為存儲(chǔ)模型,以及將存儲(chǔ)模型...
    barrylyl閱讀 979評(píng)論 0 3
  • 所謂的持久化,就是將數(shù)據(jù)保存到硬盤中,使得在應(yīng)用程序或機(jī)器重啟后可以繼續(xù)訪問(wèn)之前保存的數(shù)據(jù)。在iOS開(kāi)發(fā)中,有很多...
    沙漠騎士閱讀 413評(píng)論 0 1
  • 本文轉(zhuǎn)自iOS中幾種數(shù)據(jù)持久化方案,僅用作個(gè)人記錄學(xué)習(xí)之用。 概論 所謂的持久化,就是將數(shù)據(jù)保存到硬盤中,使得在應(yīng)...
    iOS_肖晨閱讀 761評(píng)論 0 46
  • iOS本地?cái)?shù)據(jù)保存有多種方式,比如NSUserDefaults、歸檔、plist文件保存、數(shù)據(jù)庫(kù)、CoreData...
    iOS小飛羊閱讀 358評(píng)論 0 0

友情鏈接更多精彩內(nèi)容