iOS数据本地持久化

 

p1:归档、Preference(NSUserDefault)、沙盒存储

iOS开发中本地存储主要有三种形式

  • XML属性列表(plist)归档
  • Preference(偏好设置)
  • NSKeyedArchiver归档(NSCoding)

应用沙盒

什么是应用沙盒

要想在本地存储数据,那就要知道一下什么是应用沙盒 ,其实很好理解应用沙盒就是应用的文件夹,与其余文件系统隔离。每个iOS应用都有本身的应用沙盒,应用必须待在本身的沙盒里,其它应用不能访问该沙盒。
如何获取应用沙盒路径,能够经过打印NSHomeDirectory()来获取应用沙盒路径,下图为打印路径结果:html


屏幕快照 2015-12-03 22.10.07.png


Melody_Zhy 是用户文件夹(样子是个小房子)
3CEC8EEB-C230-44BE-93B7-DF3B9A120A94 iOS8以后每次运行Xcode都会生成不一样的沙盒路径,不一样之处就是最后这个文件夹名,多是苹果为了安全着想android


应用沙盒结构分析

首先咱们先来看下,应用沙盒里面都有什么ios


屏幕快照 2015-12-03 22.27.50.png


这里提一下Finder的快捷键 shift + com + g 能够前往任意路径的文件夹,所以咱们能够打印沙盒路径以后将沙盒路径复制到Finder前往路径文件夹中,前往应用沙盒。这是一个比较耽误事的方法!幸亏有一款叫作simpholders的app,它能够很简单的访问应用的沙盒路径,记得去下载simpholders2哦,第一代iOS8以后就不能用了,app很简单易懂,用下就会了~
如今咱们来看看应用沙盒里面这些文件夹都是作什么用的git

  • Documents :保存应用运行时生成的须要持久化的数据,iTunes同步设备时会备份该目录。例如,游戏应用可将游戏存档保存在该目录
  • Library/Caches : 保存应用运行时生成的须要持久化的数据,iTunes同步设备时不会备份该目录。通常存储体积大、不须要备份的非重要数据 
  • Library/Preference : 保存应用的全部偏好设置,iOS的Settings(设置)应用会在该目录中查找应用的设置信息。iTunes同步设备时会备份该目录
  • tmp : 保存应用运行时所需的临时数据,使用完毕后再将相应的文件从该目录删除。应用没有运行时,系统也可能会清除该目录下的文件。iTunes同步设备时不会备份该目录

应用沙盒目录的常见获取方式

沙盒根目录的获取方式

正如上面咱们所说:github

NSString *home = NSHomeDirectory();
Documents文件夹的获取方式(3种)

第一种( !笨!)sql

// 利用沙盒根目录拼接字符串 NSString *homePath = NSHomeDirectory(); NSString *docPath = [homePath stringByAppendingString:@"/Documents"];

第二种( !还👌!)数据库

// 利用沙盒根目录拼接”Documents”字符串 NSString *homePath = NSHomeDirectory(); NSString *docPath = [homePath stringByAppendingPathComponent:@"Documents"];

可是不建议使用这种方法,由于不定哪天苹果大大就把文件名称改了呢-_-!编程

第三种( !~推荐~ !)数组

// NSDocumentDirectory 要查找的文件 // NSUserDomainMask 表明从用户文件夹下找 // 在iOS中,只有一个目录跟传入的参数匹配,因此这个集合里面只有一个元素 NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0]; NSString *filePath = [path stringByAppendingPathComponent:@"xxx.plist"];

这里我来详细的说下NSSearchPathForDirectoriesInDomains这个方法的几个参数 :
<#NSSearchPathDirectory directory#> 这个参数表明要查找的文件,是个枚举! 枚举你懂的点击去看看就知道了~
<#NSSearchPathDomainMask domainMask#> 这个参数表明从用户文件夹下找,也是枚举!
最后一个参数若是是NO的话,打印的路径会是这种形式~/Documents,咱们通常都会用YES,这样能够获取完整路径字符串!
这个方法的返回值是一个数组,但在iOS中,只有一个目录跟传入的参数匹配,因此这个集合里面只有一个元素,因此咱们取第一个元素!缓存


Library/Caches文件夹的获取方式(跟上面的方法类似)

这里我只用上面的第三种方法!注意第一个参数!

NSString *path = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0]; NSString *filePath = [path stringByAppendingPathComponent:@"student.data"];

tmp文件夹的获取方式
NSString *tmp= NSTemporaryDirectory();

Library/Preference文件夹的获取方式

经过NSUserDefaults类存取该目录下的设置信息!
!!!这个下面会有介绍!!!

XML属性列表(plist)归档

plist文件

plist的根Type只能是字典(NSDictionary)或者是数组(NSArray)因此归档时咱们只能将数组或字典保存到plist文件中,可是NSString也能经过归档保存到plist文件中同时它也能够经过stringWithContentsOfFile解档,它保存到plist中时Type是空的,Value是有值的!


plist文件的归档
NSArray *arr = [[NSArray alloc] initWithObjects:@"1", @"2", nil]; // NSDocumentDirectory 要查找的文件 // NSUserDomainMask 表明从用户文件夹下找 // 在iOS中,只有一个目录跟传入的参数匹配,因此这个集合里面只有一个元素 NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0]; NSString *filePath = [path stringByAppendingPathComponent:@"xxx.plist"]; [arr writeToFile:filePath atomically:YES];
plist文件的解档
NSString *filePath = [path stringByAppendingPathComponent:@"xxx.plist"]; // 解档 NSArray *arr = [NSArray arrayWithContentsOfFile:filePath]; NSLog(@"%@", arr);

Preference(偏好设置)

OC中有一个NSUserDefaults的单例,它能够用来存储用户的偏好设置,例如:用户名,字体的大小,用户的一些设置等,下面我用两个UISwitch来演示如何保存用户设置开关的关闭状态


保存用户偏好设置
// 获取用户偏好设置对象 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; // 保存用户偏好设置 [defaults setBool:self.one.isOn forKey:@"one"]; [defaults setBool:self.two.isOn forKey:@"two"]; // 注意:UserDefaults设置数据时,不是当即写入,而是根据时间戳定时地把缓存中的数据写入本地磁盘。因此调用了set方法以后数据有可能尚未写入磁盘应用程序就终止了。 // 出现以上问题,能够经过调用synchornize方法强制写入 // 如今这个版本不用写也会立刻写入 不过以前的版本不会 [defaults synchronize];
读取用户偏好设置
// 读取用户偏好设置 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; self.one.on = [defaults boolForKey:@"one"]; self.two.on = [defaults boolForKey:@"two"];

NSKeyedArchiver归档(NSCoding)

只有遵照了NSCoding协议的类才能够用NSKeyedArchiver归档和NSKeyedUnarchiver解档,若是对象是NSString、NSDictionary、NSArray、NSData、NSNumber等类型,能够直接用NSKeyedArchiver归档和NSKeyedUnarchiver解档~
下面我举的🌰是归档解档一个Student模型,所以该模型应该遵照NSCoding协议


实现encodeWithCoder和initWithCoder方法
- (void)encodeWithCoder:(NSCoder *)coder { [coder encodeObject:self.name forKey:@"name"]; [coder encodeInteger:self.age forKey:@"age"]; } - (instancetype)initWithCoder:(NSCoder *)coder { self = [super init]; if (self) { self.age = [coder decodeIntegerForKey:@"age"]; self.name = [coder decodeObjectForKey:@"name"]; } return self; }
归档
Student *s1 = [[Student alloc] init];
s1.name = @"zzz"; s1.age = 18; NSString *path = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0]; // 这个文件后缀能够是任意的,只要不与经常使用文件的后缀重复便可,我喜欢用data NSString *filePath = [path stringByAppendingPathComponent:@"student.data"]; // 归档 [NSKeyedArchiver archiveRootObject:s1 toFile:filePath];
解档
NSString *path = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0]; NSString *filePath = [path stringByAppendingPathComponent:@"student.data"]; // 解档 Student *s = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath]; NSLog(@"%@----%ld", s.name, s.age);

相关连接:iOS开发中本地数据存储的总结

 

以上的全部存储方法,都是覆盖存储。若是想要增长一条数据就必须把整个文件读出来,而后修改数据后再把整个内容覆盖写入文件。因此它们都不适合存储大量的内容,大量存储须要用SQLite、CoreData等!

p2:KeyChain

ios的机密数据用KeyChain保存

相关连接:ios KeyChain中保存数据

              ios KeyChain项目中应用到的内容

 

 

p3:ASIHTTPRequest本地缓存

相关连接:使用ASIHTTPRequest和ASIDownloadCache实现本地缓存

一、设置全局的Cache
    在AppDelegate.h中添加一个全局变量

@interface AppDelegate : UIResponder   
{  
    ASIDownloadCache *myCache;  
}  
@property (strong, nonatomic) UIWindow *window;  
@property (nonatomic,retain) ASIDownloadCache *myCache;
   在AppDelegate.m中的- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions方法中添加以下代码
//自定义缓存  
ASIDownloadCache *cache = [[ASIDownloadCache alloc] init];  
self.myCache = cache;  
[cache release];  
//设置缓存路径  
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);  
NSString *documentDirectory = [paths objectAtIndex:0];  
[self.myCache setStoragePath:[documentDirectory stringByAppendingPathComponent:@"resource"]];  
[self.myCache setDefaultCachePolicy:ASIOnlyLoadIfNotCachedCachePolicy];

     在AppDelegate.m中的dealloc方法中添加以下语句  

 [myCache release];
    到这里为止,就完成了全局变量的声明。

 

    二、设置缓存策略

    在实现ASIHTTPRequest请求的地方设置request的存储方式,代码以下

NSString *str = @"http://....../getPictureNews.aspx";  

NSURL *url = [NSURL URLWithString:str];  

ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];  

//获取全局变量  

AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];  

//设置缓存方式  
[request setDownloadCache:appDelegate.myCache];  
//设置缓存数据存储策略,这里采起的是若是无更新或没法联网就读取缓存数据  
[request setCacheStoragePolicy:ASICachePermanentlyCacheStoragePolicy];  
request.delegate = self;  
[request startAsynchronous];
    三、清理缓存数据

 

    我在这里采用的是手动清理数据的方式,在适当的地方添加以下代码,我将清理缓存放在了应用的设置模块:

AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];  
[appDelegate.myCache clearCachedResponsesForStoragePolicy:ASICachePermanentlyCacheStoragePolicy]; 

     这里清理的是ASICachePermanentlyCacheStoragePolicy这种存储策略的缓存数据,若是更换其余的参数的话,便可清理对应存储策略的缓存数据。

 

p4:SQLite/FMDB

SQLite3

以前的全部存储方法,都是覆盖存储。若是想要增长一条数据就必须把整个文件读出来,而后修改数据后再把整个内容覆盖写入文件。因此它们都不适合存储大量的内容。

1.字段类型

表面上SQLite将数据分为如下几种类型:

  • integer : 整数

  • real : 实数(浮点数)

  • text : 文本字符串

  • blob : 二进制数据,好比文件,图片之类的

实际上SQLite是无类型的。即无论你在创表时指定的字段类型是什么,存储是依然能够存储任意类型的数据。并且在创表时也能够不指定字段类型。SQLite之因此什么类型就是为了良好的编程规范和方便开发人员交流,因此平时在使用时最好设置正确的字段类型!主键必须设置成integer

2. 准备工做

准备工做就是导入依赖库啦,在iOS中要使用SQLite3,须要添加库文件:libsqlite3.dylib并导入主头文件,这是一个C语言的库,因此直接使用SQLite3仍是比较麻烦的。

3.使用

  • 建立数据库并打开

操做数据库以前必须先指定数据库文件和要操做的表,因此使用SQLite3,首先要打开数据库文件,而后指定或建立一张表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
*  打开数据库并建立一个表
*/
- (void)openDatabase {
    //1.设置文件名
    NSString *filename = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@ "person.db" ];
    //2.打开数据库文件,若是没有会自动建立一个文件
    NSInteger result = sqlite3_open(filename.UTF8String, &_sqlite3);
    if  (result == SQLITE_OK) {
        NSLog(@ "打开数据库成功!" );
        //3.建立一个数据库表
        char *errmsg = NULL;
        sqlite3_exec(_sqlite3,  "CREATE TABLE IF NOT EXISTS t_person(id integer primary key autoincrement, name text, age integer)" , NULL, NULL, &errmsg);
        if  (errmsg) {
            NSLog(@ "错误:%s" , errmsg);
        else  {
            NSLog(@ "创表成功!" );
        }
    else  {
        NSLog(@ "打开数据库失败!" );
    }
}
  • 执行指令

使用 sqlite3_exec() 方法能够执行任何SQL语句,好比创表、更新、插入和删除操做。可是通常不用它执行查询语句,由于它不会返回查询到的数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
*  往表中插入1000条数据
*/
- (void)insertData {
NSString *nameStr;
NSInteger age;
for  (NSInteger i = 0; i < 1000; i++) {
   nameStr = [NSString stringWithFormat:@ "Bourne-%d" , arc4random_uniform(10000)];
   age = arc4random_uniform(80) + 20;
   NSString *sql = [NSString stringWithFormat:@ "INSERT INTO t_person (name, age) VALUES('%@', '%ld')" , nameStr, age];
   char *errmsg = NULL;
   sqlite3_exec(_sqlite3, sql.UTF8String, NULL, NULL, &errmsg);
   if  (errmsg) {
       NSLog(@ "错误:%s" , errmsg);
   }
}
NSLog(@ "插入完毕!" );
}
  • 查询指令

前面说过通常不使用 sqlite3_exec() 方法查询数据。由于查询数据必需要得到查询结果,因此查询相对比较麻烦。示例代码以下:

    • sqlite3_prepare_v2() : 检查sql的合法性

    • sqlite3_step() : 逐行获取查询结果,不断重复,直到最后一条记录

    • sqlite3_coloum_xxx() : 获取对应类型的内容,iCol对应的就是SQL语句中字段的顺序,从0开始。根据实际查询字段的属性,使用sqlite3_column_xxx取得对应的内容便可。

    • sqlite3_finalize() : 释放stmt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
*  从表中读取数据到数组中
*/
- (void)readData {
    NSMutableArray *mArray = [NSMutableArray arrayWithCapacity:1000];
    char *sql =  "select name, age from t_person;" ;
    sqlite3_stmt *stmt;
    NSInteger result = sqlite3_prepare_v2(_sqlite3, sql, -1, &stmt, NULL);
    if  (result == SQLITE_OK) {
        while  (sqlite3_step(stmt) == SQLITE_ROW) {
            char *name = (char *)sqlite3_column_text(stmt, 0);
            NSInteger age = sqlite3_column_int(stmt, 1);
            //建立对象
            Person *person = [Person personWithName:[NSString stringWithUTF8String:name] Age:age];
            [mArray addObject:person];
        }
        self.dataList = mArray;
    }
    sqlite3_finalize(stmt);
}

4.总结

总得来讲,SQLite3的使用仍是比较麻烦的,由于都是些c语言的函数,理解起来有些困难。不过在通常开发过程当中,使用的都是第三方开源库 FMDB,封装了这些基本的c语言方法,使得咱们在使用时更加容易理解,提升开发效率。

FMDB

1.简介

FMDB是iOS平台的SQLite数据库框架,它是以OC的方式封装了SQLite的C语言API,它相对于cocoa自带的C语言框架有以下的优势:

使用起来更加面向对象,省去了不少麻烦、冗余的C语言代码

对比苹果自带的Core Data框架,更加轻量级和灵活

提供了多线程安全的数据库操做方法,有效地防止数据混乱

注:FMDB的gitHub地址

2.核心类

FMDB有三个主要的类:

  • FMDatabase

一个FMDatabase对象就表明一个单独的SQLite数据库,用来执行SQL语句

  • FMResultSet

使用FMDatabase执行查询后的结果集

  • FMDatabaseQueue

用于在多线程中执行多个查询或更新,它是线程安全的

3.打开数据库

和c语言框架同样,FMDB经过指定SQLite数据库文件路径来建立FMDatabase对象,但FMDB更加容易理解,使用起来更容易,使用以前同样须要导入sqlite3.dylib。打开数据库方法以下:

1
2
3
4
5
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@ "person.db" ];
FMDatabase *database = [FMDatabase databaseWithPath:path];    
if  (![database open]) {
     NSLog(@ "数据库打开失败!" );
}

值得注意的是,Path的值能够传入如下三种状况:

  • 具体文件路径,若是不存在会自动建立

  • 空字符串@"",会在临时目录建立一个空的数据库,当FMDatabase链接关闭时,数据库文件也被删除

  • nil,会建立一个内存中临时数据库,当FMDatabase链接关闭时,数据库会被销毁

4.更新

在FMDB中,除查询之外的全部操做,都称为“更新”, 如:create、drop、insert、update、delete等操做,使用executeUpdate:方法执行更新:

1
2
3
4
5
6
7
8
//经常使用方法有如下3种:   
- (BOOL)executeUpdate:(NSString*)sql, ...
- (BOOL)executeUpdateWithFormat:(NSString*)format, ...
- (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments
//示例
[database executeUpdate:@ "CREATE TABLE IF NOT EXISTS t_person(id integer primary key autoincrement, name text, age integer)" ];   
//或者  
[database executeUpdate:@ "INSERT INTO t_person(name, age) VALUES(?, ?)" , @ "Bourne" , [NSNumber numberWithInt:42]];

5.查询

查询方法也有3种,使用起来至关简单:

1
2
3
- (FMResultSet *)executeQuery:(NSString*)sql, ...
- (FMResultSet *)executeQueryWithFormat:(NSString*)format, ...
- (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)arguments

查询示例:

1
2
3
4
5
6
7
//1.执行查询
FMResultSet *result = [database executeQuery:@ "SELECT * FROM t_person" ];
//2.遍历结果集
while  ([result next]) {
     NSString *name = [result stringForColumn:@ "name" ];
     int age = [result intForColumn:@ "age" ];
}

6.线程安全

在多个线程中同时使用一个FMDatabase实例是不明智的。不要让多个线程分享同一个FMDatabase实例,它没法在多个线程中同时使用。 若是在多个线程中同时使用一个FMDatabase实例,会形成数据混乱等问题。因此,请使用 FMDatabaseQueue,它是线程安全的。如下是使用方法:

  • 建立队列。

1
FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:aPath];
  • 使用队列

1
2
3
4
5
6
7
8
[queue inDatabase:^(FMDatabase *database) {    
           [database executeUpdate:@ "INSERT INTO t_person(name, age) VALUES (?, ?)" , @ "Bourne_1" , [NSNumber numberWithInt:1]];    
           [database executeUpdate:@ "INSERT INTO t_person(name, age) VALUES (?, ?)" , @ "Bourne_2" , [NSNumber numberWithInt:2]];    
           [database executeUpdate:@ "INSERT INTO t_person(name, age) VALUES (?, ?)" , @ "Bourne_3" , [NSNumber numberWithInt:3]];      
           FMResultSet *result = [database executeQuery:@ "select * from t_person" ];    
          while ([result next]) {   
          }    
}];

并且能够轻松地把简单任务包装到事务里:

1
2
3
4
5
6
7
8
9
10
[queue inTransaction:^(FMDatabase *database, BOOL *rollback) {    
           [database executeUpdate:@ "INSERT INTO t_person(name, age) VALUES (?, ?)" , @ "Bourne_1" , [NSNumber numberWithInt:1]];    
           [database executeUpdate:@ "INSERT INTO t_person(name, age) VALUES (?, ?)" , @ "Bourne_2" , [NSNumber numberWithInt:2]];    
           [database executeUpdate:@ "INSERT INTO t_person(name, age) VALUES (?, ?)" , @ "Bourne_3" , [NSNumber numberWithInt:3]];      
           FMResultSet *result = [database executeQuery:@ "select * from t_person" ];    
              while ([result next]) {   
              }   
            //回滚
            *rollback = YES;  
     }];

FMDatabaseQueue 后台会创建系列化的G-C-D队列,并执行你传给G-C-D队列的块。这意味着 你从多线程同时调用调用方法,GDC也会按它接收的块的顺序来执行。

 

 

p5:CoreData

 

1、CoreData的简单使用

准备工做

  • 建立数据库

    1. 新建文件,选择CoreData -> DataModel
    2. 添加实体(表),Add Entity
    3. 给表中添加属性,点击Attributes下方的‘+’
  • 建立模型文件

    1. 新建文件,选择CoreData -> NSManaged Object subclass
    2. 根据提示,选择实体
  • 经过代码,关联数据库和实体

    - (void)viewDidLoad { [super viewDidLoad]; /* * 关联的时候,若是本地没有数据库文件,Coreadata本身会建立 */ // 1. 上下文 NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init]; // 2. 上下文关连数据库 // 2.1 model模型文件 NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:nil]; // 2.2 持久化存储调度器 // 持久化,把数据保存到一个文件,而不是内存 NSPersistentStoreCoordinator *store = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model]; // 2.3 设置CoreData数据库的名字和路径 NSString *doc = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; NSString *sqlitePath = [doc stringByAppendingPathComponent:@"company.sqlite"]; [store addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[NSURL fileURLWithPath:sqlitePath] options:nil error:nil]; context.persistentStoreCoordinator = store; _context = context; }

CoreData的基本操做(CURD)

  • 添加元素 - Create

    -(IBAction)addEmployee{ // 建立一个员工对象 //Employee *emp = [[Employee alloc] init]; 不能用此方法建立 Employee *emp = [NSEntityDescription insertNewObjectForEntityForName:@"Employee" inManagedObjectContext:_context]; emp.name = @"wangwu"; emp.height = @1.80; emp.birthday = [NSDate date]; // 直接保存数据库 NSError *error = nil; [_context save:&error]; if (error) { NSLog(@"%@",error); } }
  • 读取数据 - Read

    -(IBAction)readEmployee{ // 1.FetchRequest 获取请求对象 NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Employee"]; // 2.设置过滤条件 // 查找zhangsan NSPredicate *pre = [NSPredicate predicateWithFormat:@"name = %@", @"zhangsan"]; request.predicate = pre; // 3.设置排序 // 身高的升序排序 NSSortDescriptor *heigtSort = [NSSortDescriptor sortDescriptorWithKey:@"height" ascending:NO]; request.sortDescriptors = @[heigtSort]; // 4.执行请求 NSError *error = nil; NSArray *emps = [_context executeFetchRequest:request error:&error]; if (error) { NSLog(@"error"); } //NSLog(@"%@",emps); //遍历员工 for (Employee *emp in emps) { NSLog(@"名字 %@ 身高 %@ 生日 %@",emp.name,emp.height,emp.birthday); } }
  • 修改数据 - Update

    -(IBAction)updateEmployee{ // 改变zhangsan的身高为2m // 1.查找到zhangsan // 1.1FectchRequest 抓取请求对象 NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Employee"]; // 1.2设置过滤条件 // 查找zhangsan NSPredicate *pre = [NSPredicate predicateWithFormat:@"name = %@", @"zhangsan"]; request.predicate = pre; // 1.3执行请求 NSArray *emps = [_context executeFetchRequest:request error:nil]; // 2.更新身高 for (Employee *e in emps) { e.height = @2.0; } // 3.保存 NSError *error = nil; [_context save:&error]; if (error) { NSLog(@"%@",error); } }
  • 删除数据 - Delete

    -(IBAction)deleteEmployee{ // 删除 lisi // 1.查找lisi // 1.1FectchRequest 抓取请求对象 NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Employee"]; // 1.2设置过滤条件 // 查找zhangsan NSPredicate *pre = [NSPredicate predicateWithFormat:@"name = %@", @"lisi"]; request.predicate = pre; // 1.3执行请求 NSArray *emps = [_context executeFetchRequest:request error:nil]; // 2.删除 for (Employee *e in emps) { [_context deleteObject:e]; } // 3.保存 NSError *error = nil; [_context save:&error]; if (error) { NSLog(@"%@",error); } }

2、CoreData的表关联

准备工做

  • 建立数据库

    1. 新建文件,选择CoreData -> DataModel
    2. 添加实体(表),Add Entity注意:这里根据关联添加多个实体
    3. 给表中添加属性,点击Attributes下方的‘+’
  • 建立模型文件

    1. 新建文件,选择CoreData -> NSManaged Object subclass
    2. 根据提示,选择实体,注意:这里先选择被关联的实体,最后添加最上层的实体
  • 经过代码,关联数据库和实体

    - (void)viewDidLoad { [super viewDidLoad]; /* * 关联的时候,若是本地没有数据库文件,Coreadata本身会建立 */ // 1. 上下文 NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init]; // 2. 上下文关连数据库 // 2.1 model模型文件 NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:nil]; // 2.2 持久化存储调度器 // 持久化,把数据保存到一个文件,而不是内存 NSPersistentStoreCoordinator *store = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model]; // 2.3 设置CoreData数据库的名字和路径 NSString *doc = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; NSString *sqlitePath = [doc stringByAppendingPathComponent:@"company.sqlite"]; [store addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[NSURL fileURLWithPath:sqlitePath] options:nil error:nil]; context.persistentStoreCoordinator = store; _context = context; }

基本操做

  • 添加元素 - Create

    -(IBAction)addEmployee{ // 1. 建立两个部门 ios android //1.1 iOS部门 Department *iosDepart = [NSEntityDescription insertNewObjectForEntityForName:@"Department" inManagedObjectContext:_context]; iosDepart.name = @"ios"; iosDepart.departNo = @"0001"; iosDepart.createDate = [NSDate date]; //1.2 Android部门 Department *andrDepart = [NSEntityDescription insertNewObjectForEntityForName:@"Department" inManagedObjectContext:_context]; andrDepart.name = @"android"; andrDepart.departNo = @"0002"; andrDepart.createDate = [NSDate date]; //2. 建立两个员工对象 zhangsan属于ios部门 lisi属于android部门 //2.1 zhangsan Employee *zhangsan = [NSEntityDescription insertNewObjectForEntityForName:@"Employee" inManagedObjectContext:_context]; zhangsan.name = @"zhangsan"; zhangsan.height = @(1.90); zhangsan.birthday = [NSDate date]; zhangsan.depart = iosDepart; //2.2 lisi Employee *lisi = [NSEntityDescription insertNewObjectForEntityForName:@"Employee" inManagedObjectContext:_context]; lisi.name = @"lisi"; lisi.height = @2.0; lisi.birthday = [NSDate date]; lisi.depart = andrDepart; //3. 保存数据库 NSError *error = nil; [_context save:&error]; if (error) { NSLog(@"%@",error); } }
  • 读取信息 - Read

    -(IBAction)readEmployee{ // 读取ios部门的员工 // 1.FectchRequest 抓取请求对象 NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Employee"]; // 2.设置过滤条件 NSPredicate *pre = [NSPredicate predicateWithFormat:@"depart.name = %@",@"android"]; request.predicate = pre; // 4.执行请求 NSError *error = nil; NSArray *emps = [_context executeFetchRequest:request error:&error]; if (error) { NSLog(@"error"); } //遍历员工 for (Employee *emp in emps) { NSLog(@"名字 %@ 部门 %@",emp.name,emp.depart.name); } }
  • 其余功能与前几种相似,这里不在赘述

3、CoreData的模糊查询

准备工做和上面相似,主要是查询方式不一样

  • 模糊查询

    -(IBAction)readEmployee{ // 1.FectchRequest 抓取请求对象 NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Employee"]; // 2.设置排序 // 按照身高的升序排序 NSSortDescriptor *heigtSort = [NSSortDescriptor sortDescriptorWithKey:@"height" ascending:NO]; request.sortDescriptors = @[heigtSort]; // 3.模糊查询 // 3.1 名字以"wang"开头 // NSPredicate *pre = [NSPredicate predicateWithFormat:@"name BEGINSWITH %@",@"wangwu1"]; // request.predicate = pre; // 名字以"1"结尾 // NSPredicate *pre = [NSPredicate predicateWithFormat:@"name ENDSWITH %@",@"1"]; // request.predicate = pre; // 名字包含"wu1" // NSPredicate *pre = [NSPredicate predicateWithFormat:@"name CONTAINS %@",@"wu1"]; // request.predicate = pre; // like 匹配 NSPredicate *pre = [NSPredicate predicateWithFormat:@"name like %@",@"*wu12"]; request.predicate = pre; // 4.执行请求 NSError *error = nil; NSArray *emps = [_context executeFetchRequest:request error:&error]; if (error) { NSLog(@"error"); } //遍历员工 for (Employee *emp in emps) { NSLog(@"名字 %@ 身高 %@ 生日 %@",emp.name,emp.height,emp.birthday); } }
  • 分页查询

    -(void)pageSeacher{ // 1. FectchRequest 抓取请求对象 NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Employee"]; // 2. 设置排序 // 身高的升序排序 NSSortDescriptor *heigtSort = [NSSortDescriptor sortDescriptorWithKey:@"height" ascending:NO]; request.sortDescriptors = @[heigtSort]; // 3. 分页查询 // 总有共有15数据 // 每次获取6条数据 // 第一页 0,6 // 第二页 6,6 // 第三页 12,6 3条数据 // 3.1 分页的起始索引 request.fetchOffset = 12; // 3.2 分页的条数 request.fetchLimit = 6; // 4. 执行请求 NSError *error = nil; NSArray *emps = [_context executeFetchRequest:request error:&error]; if (error) { NSLog(@"error"); } // 5. 遍历员工 for (Employee *emp in emps) { NSLog(@"名字 %@ 身高 %@ 生日 %@",emp.name,emp.height,emp.birthday); } }

4、多个数据库的使用

注意:

建立多个数据库,即建立多个DataModel
一个数据库对应一个上下文
须要根据bundle名建立上下文
添加或读取信息,须要根据不一样的上下文,访问不一样的实体

  • 关联数据库和实体

    - (void)viewDidLoad { [super viewDidLoad]; // 一个数据库对应一个上下文 _companyContext = [self setupContextWithModelName:@"Company"]; _weiboContext = [self setupContextWithModelName:@"Weibo"]; } /** * 根据模型文件,返回一个上下文 */ -(NSManagedObjectContext *)setupContextWithModelName:(NSString *)modelName{ // 1. 上下文 NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init]; // 2. 上下文关连数据库 // 2.1 model模型文件 // 注意:若是使用下面的方法,若是 bundles为nil 会把bundles里面的全部模型文件的表放在一个数据库 //NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:nil]; // 改成如下的方法获取: NSURL *companyURL = [[NSBundle mainBundle] URLForResource:modelName withExtension:@"momd"]; NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:companyURL]; // 2.2 持久化存储调度器 // 持久化,把数据保存到一个文件,而不是内存 NSPersistentStoreCoordinator *store = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model]; // 2.3 告诉Coredata数据库的名字和路径 NSString *doc = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; NSString *sqliteName = [NSString stringWithFormat:@"%@.sqlite",modelName]; NSString *sqlitePath = [doc stringByAppendingPathComponent:sqliteName]; [store addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[NSURL fileURLWithPath:sqlitePath] options:nil error:nil]; context.persistentStoreCoordinator = store; // 3. 返回上下文 return context; }
  • 添加元素

    -(IBAction)addEmployee{ // 1. 添加员工 Employee *emp = [NSEntityDescription insertNewObjectForEntityForName:@"Employee" inManagedObjectContext:_companyContext]; emp.name = @"zhagsan"; emp.height = @2.3; emp.birthday = [NSDate date]; // 直接保存数据库 [_companyContext save:nil]; // 2. 发微博 Status *status =[NSEntityDescription insertNewObjectForEntityForName:@"Status" inManagedObjectContext:_weiboContext]; status.text = @"发了一条微博!"; status.createDate = [NSDate date]; [_weiboContext save:nil]; }

 

 
 
相关连接: iOS中的数据存储(上)
               iOS中几种数据持久化方案
              CoreData的简单使用
      IOS MagicRecord 详解
相关文章
相关标签/搜索