ios本地存储的五种方式

  1. 本文参考自:iOS 本地持久化存储
    作者:哆啦_
  2. 本文参考自:iOS CoreData数据库之创建详解
    作者:tanyufeng

ios数据存储的5种方式

  1. NSUserDefaults(Preference偏好设置)
  2. plist存储
  3. 归档
  4. SQLite3
  5. CoreData

应用沙盒

Document:适合存储重要的数据, iTunes同步应用时会同步该文件下的内容,(比如游戏中的存档)
Library/Caches:适合存储体积大,不需要备份的非重要数据,iTunes不会同步该文件
Library/Preferences:通常保存应用的设置信息, iTunes会同步
tmp:保存应用的临时文件,用完就删除,系统可能在应用没在运行时删除该目录下的文件,iTunes不会同步

获取沙盒路径

Document:

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentFilePath = paths.firstObject;

NSuserDefault

NSuserDefault适合存储轻量级的本地数据,支持的数据类型有:NSNumber,NSString,NSDate,NSArray,NSDictionary,BOOL,NSData

沙盒路径为 Library/Preferences
文件格式为 .plist

优点:

  1. 不需要关心文件名
  2. 快速进行键值对存储
  3. 直接存储基本数据类型

缺点:

  1. 不能存储自定义数据
  2. 取出的数据都是不可变的
- (IBAction)userDefaultSave:(id)sender {
    NSArray *testArray = @[@"test1", @"test2", @"test3"];
    [[NSUserDefaults standardUserDefaults] setObject:testArray forKey:@"arrayKey"];
    [[NSUserDefaults standardUserDefaults] synchronize];
}
- (IBAction)userDefaultLoad:(id)sender {
    NSArray *testArray = [[NSUserDefaults standardUserDefaults] objectForKey:@"arrayKey"];
    NSLog(@"%@", testArray);
}

plist存储

plist支持的数据类型:

NSArray;
NSMutableArray;
NSDictionary;
NSMutableDictionary;
NSData;
NSMutableData;
NSString;
NSMutableString;
NSNumber;
NSDate;
不支持BOOL
而且最外层好像要用`NSArray 或 NSDictionary,偷个懒还没验证
- (IBAction)plistSave:(id)sender {
    NSString *cachePath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;
    NSString *filePath = [cachePath stringByAppendingPathComponent:@"testPlist.plist"];
    
    NSMutableDictionary *dict = [NSMutableDictionary dictionary];
    [dict setObject:@"ran" forKey:@"name"];
    [dict setObject:@"18" forKey:@"age"];
    [dict writeToFile:filePath atomically:YES];
}

- (IBAction)plistLoad:(id)sender {
    NSString *cachePath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;
    NSString *filePath = [cachePath stringByAppendingPathComponent:@"testPlist.plist"];
    
    NSDictionary *t = [NSDictionary dictionaryWithContentsOfFile:filePath];
    NSLog(@"%@",t);
}

归档

存储自定义对象

  1. 首先新建Person类,并遵守NSCoding协议
@interface Person : NSObject<NSCoding>

@property(nonatomic, strong)NSString *name;
@property(nonatomic, strong)NSString *age;

@end
  1. 实现协议方法
@implementation Person

- (instancetype)initWithCoder:(NSCoder *)coder
{
    self = [super init];
    if (self) {
        _name = [coder decodeObjectForKey:@"name"];
        _age = [coder decodeObjectForKey:@"age"];
    }
    return self;
}

- (void)encodeWithCoder:(NSCoder *)coder
{

    [coder encodeObject:self.name forKey:@"name"];
    [coder encodeObject:self.age forKey:@"age"];

}
@end
  1. 归档解档
- (IBAction)archive:(id)sender {
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentFilePath = paths.firstObject;
    NSString *filePath = [documentFilePath stringByAppendingPathComponent:@"personModel"];
    
    Person *p1 = [[Person alloc] init];
    p1.name = @"ran";
    p1.age = @"18";
    
    [NSKeyedArchiver archiveRootObject:p1 toFile:filePath];
}

- (IBAction)unarchive:(id)sender {
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentFilePath = paths.firstObject  ;
    NSString *filePath = [documentFilePath stringByAppendingPathComponent:@"personModel"];
    
    Person *p1 = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath] ;
    
    NSLog(@"%@", p1.name);
    NSLog(@"%@", p1.age);
}

但是这种方法只能存储一个对象,存储多个对象要采用如下的方法:

- (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);
}

SQLite3

数据库(splite):
splite是一个轻量级,跨平台的小型数据库,可移植性比较高,有着和MySpl几乎相同的数据库语句,以及无需服务器即可使用的优点:

数据库的优点:

  1. 该方案可以存储大量的数据,存储和检索的速度非常快.
  2. 能对数据进行大量的聚合,这样比起使用对象来讲操作要快.

数据库的缺点:

  1. 它没有提供数据库的创建方式
  2. 它的底层是基于C语言框架设计的, 没有面向对象的API, 用起来非常麻烦
  3. 发杂的数据模型的数据建表,非常麻烦
    在实际开发中我们都是使用的是FMDB第三方开源的数据库,该数据库是基于splite封装的面向对象的框架.
#import "SqliteVC.h"
#import "Person.h"
@interface SqliteVC() {
    
    sqlite3 *_db;

}
@end

@implementation SqliteVC

- (void)viewDidLoad {
    [super viewDidLoad];
    
    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(@"打开数据库失败");
    }
}
- (IBAction)insert:(id)sender {
    for (int i = 0; i < 30; i++) {
        
        NSString *name = [NSString stringWithFormat:@"person-%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)delete:(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)update:(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(@"更改失败");
    }
}


coreData

coreData是苹果官方在iOS5之后推出的综合性数据库,其使用了对象关系映射技术,将对象转换成数据,将数据存储在本地的数据库中
coreData为了提高效率,需要将数据存储在不同的数据库中,比如:在使用的时候,最好是将本地的数据保存到内存中,这样的目的是访问速度比较快.

CoreData与SQLite进行对比
SQLite
1、基于C接口,需要使用SQL语句,代码繁琐
2、在处理大量数据时,表关系更直观
3、在OC中不是可视化,不易理解


CoreData
1、可视化,且具有undo/redo能力
2、可以实现多种文件格式:
    * NSSQLiteStoreType
    * NSBinaryStoreType
    * NSInMemoryStoreType
    * NSXMLStoreTyp
3、苹果官方API支持,与iOS结合更紧密
CoreData核心类与结构

NSManagedObjectContext(数据上下文)

  • 对象管理上下文,负责数据的实际操作(重要)
  • 作用:插入数据,查询数据,删除数据,更新数据

NSPersistentStoreCoordinator(持久化存储助理)

  • 相当于数据库的连接器
  • 作用:设置数据存储的名字,位置,存储方式,和存储时机

NSManagedObjectModel(数据模型)

  • 数据库所有表格或数据结构,包含各实体的定义信息
  • 作用:添加实体的属性,建立属性之间的关系
  • 操作方法:视图编辑器,或代码

NSManagedObject(被管理的数据记录)

  • 数据库中的表格记录

NSEntityDescription(实体结构)

  • 相当于表格结构

NSFetchRequest(数据请求)

  • 相当于查询语句

后缀为.xcdatamodeld的包

  • 里面是.xcdatamodel文件,用数据模型编辑器编辑
  • 编译后为.momd或.mom文件
类关系图
开始创建coredata
步骤:
1.创建模型文件 [相当于一个数据库]
2.添加实体 [一张表]
3.创建实体类 [相当模型--表结构]
4.生成上下文 关联模型文件生成数据库
  1. 创建模型文件
    New File -> iOS -> Core Data ->Data Model
  1. 创建实体
创建实体

Codegen
  1. 创建实体类
创建实体类

创建结果如图所示:


创建结果
  1. 生成上下文 关联模型文件生成数据库,进行增删查改操作
#import "coredataVC.h"
#import <CoreData/CoreData.h>
#import "Student+CoreDataProperties.h"

@interface coredataVC ()

@property(nonatomic, strong)NSManagedObjectContext *context;

@end

@implementation coredataVC

- (void)viewDidLoad {
    [super viewDidLoad];

    //entity 记得勾选 language:objective-c 和 codegen:manual/none
    [self createSql];
}

- (void)createSql {
    //获取模型路径
    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"Person" withExtension:@"momd"];
    //根据模型文件创建模型对象
    NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
    
    //利用模型对象创建持久化存储助理
    NSPersistentStoreCoordinator *store = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
    
    //数据库的名称和路径
    NSString *docStr = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    NSString *sqlPath = [docStr stringByAppendingPathComponent:@"coreData.sqlite"];
    NSURL *sqlUrl = [NSURL fileURLWithPath:sqlPath];
    NSLog(@"数据库 path = %@", sqlPath);
    
    NSError *error = nil; //设置数据库相关信息 添加一个持久化存储库并设置类型和路径,NSSQLiteStoreType:SQLite作为存储库
    [store addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:sqlUrl options:nil error:&error];
    
    if (error) {
        NSLog(@"添加数据库失败:%@",error);
    } else {
        NSLog(@"添加数据库成功");
    }
    
    //3、创建上下文 保存信息 对数据库进行操作 关联持久化助理
    NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
    context.persistentStoreCoordinator = store;
    _context = context;
}

- (IBAction)insertClick:(id)sender {
    Student * student = [NSEntityDescription  insertNewObjectForEntityForName:@"Student"  inManagedObjectContext:_context];
    student.name = [NSString stringWithFormat:@"stu-%d",arc4random()%100];
    student.age = arc4random()%30;
    
    NSError *error = nil;
    if ([_context save:&error]) {
        NSLog(@"数据插入到数据库成功");
    }else{
        NSLog(@"数据插入到数据库失败");
    }
}

- (IBAction)deleteClick:(id)sender {
    //创建删除请求
    NSFetchRequest *deleRequest = [NSFetchRequest fetchRequestWithEntityName:@"Student"];
    
    //删除条件 没有任何条件就是读取所有的数据
    //NSPredicate *pre = [NSPredicate predicateWithFormat:@"age < %d", 10];
    //deleRequest.predicate = pre;
    
    //返回需要删除的对象数组
    NSArray *deleArray = [_context executeFetchRequest:deleRequest error:nil];
    
    //从数据库中删除
    for (Student *stu in deleArray) {
        [_context deleteObject:stu];
    }
    
    NSError *error = nil;
    if ([_context save:&error]) {
        NSLog(@"删除数据成功");
    }else{
        NSLog(@"删除数据失败, %@", error);
    }
}

- (IBAction)queryClick:(id)sender {
    //创建查询请求
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Student"];
    
    //查询条件 没有任何条件就是读取所有的数据
    NSPredicate *pre = [NSPredicate predicateWithFormat:@"age >= 0"];
    request.predicate = pre;
    
    // 从第几页开始显示 通过这个属性实现分页
    //request.fetchOffset = 0;
    // 每页显示多少条数据
    //request.fetchLimit = 6;
    
    //发送查询请求
    NSArray *resArray = [_context executeFetchRequest:request error:nil];
    
    //打印查询结果
    for (Student *stu in resArray) {
        NSLog(@"name=%@, age=%d",stu.name, stu.age);
    }
}

- (IBAction)updateClick:(id)sender {
    //创建查询请求
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Student"];
    
    NSPredicate *pre = [NSPredicate predicateWithFormat:@"age >= 0"];
    request.predicate = pre;
    
    //发送请求
    NSArray *resArray = [_context executeFetchRequest:request error:nil];
    
    //修改
    for (Student *stu in resArray) {
        stu.name = @"ran";
    }
    
    NSError *error = nil;
    if ([_context save:&error]) {
        NSLog(@"更新数据成功");
    }else{
        NSLog(@"更新数据失败, %@", error);
    }
}

@end

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

推荐阅读更多精彩内容