iOS 数据持久化

所谓的持久化,就是将数据保存到硬盘中,使得在应用程序或机器重启后可以继续访问之前保存的数据。在iOS开发中,有很多数据持久化的方案.
在开发中常用的存储方案主要有以下几种:
1 plist文件(属性列表)
2.NSKeyedArchiver(归档)
3.SQLite 3
4.CoreData(这个不经常用)
5.keyChain

沙盒
在介绍各种存储方法之前,有必要说明以下沙盒机制。iOS程序默认情况下只能访问程序自己的目录,这个目录被称为“沙盒”。
沙盒的目录结构如下图:


ss.png

Documents文件夹。存储用户数据和用户文档。
Library文件夹。存储系统数据。
tmp文件夹。临时文件夹,用于存储临时数据,这个文件夹不稳定,数据可能被清除。
SystemData 最近刚加的,存储系统数据

1.1存储为plist文件
plist文件是将某些特定的类,通过XML文件的方式保存在目录中。

可以被序列化的类型只有如下几种:必须要遵循
NSArray;
NSMutableArray;
NSDictionary;
NSMutableDictionary;
NSData;
NSMutableData;
NSString;
NSMutableString;
NSNumber;
NSDate;

eg:存入字符串

NSString *path = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/myFile.plist"];
    
    NSError *err = nil;
    
    //writeToFile把字符串存入文件。第一个参数表示存储的路径(包含文件名),第二个参数表示是否原子性写入(如果多个线程可能同时操作这个文件,原子性需要写YES),第三个参数表示文本的编码方式(字符串存储到硬盘上必须先转换为二进制数据,选择按照哪种编码格式进行编码)。第四个参数表示如果存储失败,失败的原因。
    if (![@"天涯明月新,朝暮最相思" writeToFile:path atomically:NO encoding:NSUTF8StringEncoding error:&err]) {
        NSLog(@"%@",err);
    }

读取字符串

 NSString *path = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/myFile.plist"];
    //从文件中读取字符串,第一个参数,读取的文件的路径,第二个参数编码方式,第三个参数,如果失败,失败的原因。
    NSString *text = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
    NSLog(@"%@",text);

eg:存入数组

   NSArray *array = @[@"惟将长夜未开眼",@"报答平生未展眉"];
    
    NSString *path = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/array.plist"];
    
    //把数组存入文件
    [array writeToFile:path atomically:NO];

读取数组

NSString *path = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/array.plist"];
    //从文件中读取数组
    NSArray *array = [NSArray arrayWithContentsOfFile:path];
    for (NSString *strs in array)
    {
            NSLog(@"%@",strs);
    }

1.2 NSUserDefaults 适用于轻量级数据了比较小的,一般存储用户信息的相关数据。

//1.获得NSUserDefaults文件
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
//2.向文件中写入内容
[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 数据序列化,只有遵守了NSCoding或 NSSecureCoding(更为安全的归档协议)协议,并且实现了协议里归档与解归档的方法的的类创建的对象才能够进行归档
    一般是对自己创建的模型进行存储是会用到,
    需要主要以下三点:
    1.必须遵循并实现NSCoding协议
    2.保存文件的扩展名可以任意指定
    3.继承时必须先调用父类的归档解档方法
    eg:建一个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
大量数据的存储我们一般用FMDB,比如聊天中的信息一般是用FMDB存储的,FMDB是在sqlite的基础上进行封装的.
一般我们在使用时会对FMDB进行再次封装,我们对数据的操作一般也就是增,删,改,查。将fmdb 封装成一个工具类。将需要存储的信息封装成一个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(@"创建表成功");
        }
        else
        {
            NSLog(@"创建表失败");
        }
         [self.db close];
    }
    else
    {
        NSLog(@"打开数据库失败");
    }
   
}
- (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 这个基本很少用

5.KeyChain
iOS keychain 是一个相对独立的空间,保存到keychain钥匙串中的信息不会因为卸载/重装app而丢失, 。相对于NSUserDefaults、plist文件保存等一般方式,keychain保存更为安全。所以我们会用keyChain保存一些私密信息,比如密码、证书、设备唯一码(把获取到用户设备的唯一ID 存到keychain 里面这样卸载或重装之后还可以获取到id,保证了一个设备一个ID)等等。keychain是用SQLite进行存储的。用苹果的话来说是一个专业的数据库,加密我们保存的数据,可以通过metadata(attributes)进行高效的搜索。keychain适合保存一些比较小的数据量的数据,如果要保存大的数据,可以考虑文件的形式存储在磁盘上,在keychain里面保存解密这个文件的密钥。
如果需要在应用里使用使用keyChain,我们需要导入Security.framework ,keychain的操作接口声明在头文件SecItem.h里。但是自己需要自己封装有点复杂。我这边使用的是第三方封装好的SSKeychain。导入即可使用
保存密码:

 if ( [SAMKeychain setPassword:@"白首如新,倾盖如故" forService:@"test" account:@"testAccount"])
    {
        NSLog(@"keyChin存储成功");
    }
    else
    {
        NSLog(@"keyChain存储失败");
    }

读取密码

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

Demo地址

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,907评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,987评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,298评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,586评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,633评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,488评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,275评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,176评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,619评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,819评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,932评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,655评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,265评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,871评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,994评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,095评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,884评论 2 354

推荐阅读更多精彩内容

  • 1.简介 数据持久存储是一种非易失性存储,在重启动计算机或设备后也不会丢失数据。持久化技术主要用于MVC模型中的m...
    公子无礼阅读 1,682评论 0 4
  • Title: iOS数据持久化 ##数据持久化概念 数据持久化就是将内存中的数据模型转换为存储模型,以及将存储模型...
    barrylyl阅读 934评论 0 3
  • 所谓的持久化,就是将数据保存到硬盘中,使得在应用程序或机器重启后可以继续访问之前保存的数据。在iOS开发中,有很多...
    沙漠骑士阅读 355评论 0 1
  • 本文转自iOS中几种数据持久化方案,仅用作个人记录学习之用。 概论 所谓的持久化,就是将数据保存到硬盘中,使得在应...
    iOS_肖晨阅读 717评论 0 46
  • iOS本地数据保存有多种方式,比如NSUserDefaults、归档、plist文件保存、数据库、CoreData...
    iOS小飞羊阅读 302评论 0 0