iOS数据本地持久化
p1:归档、Preference(NSUserDefault)、沙盒存储
iOS开发中本地存储主要有三种形式
- XML属性列表(plist)归档
- Preference(偏好设置)
- NSKeyedArchiver归档(NSCoding)
应用沙盒
什么是应用沙盒
要想在本地存储数据,那就要知道一下什么是应用沙盒 ,其实很好理解应用沙盒就是应用的文件夹,与其他文件系统隔离。每一个iOS应用都有自己的应用沙盒,应用必须待在自己的沙盒里,其它应用不能访问该沙盒。
如何获取应用沙盒路径,可以通过打印NSHomeDirectory()来获取应用沙盒路径,下图为打印路径结果:
Melody_Zhy 是用户文件夹(样子是个小房子)
3CEC8EEB-C230-44BE-93B7-DF3B9A120A94 iOS8之后每次运行Xcode都会生成不同的沙盒路径,不同之处就是最后这个文件夹名,可能是苹果为了安全着想
应用沙盒结构分析
首先我们先来看下,应用沙盒里面都有什么
这里提一下Finder的快捷键 shift + com + g 可以前往任意路径的文件夹,因此我们可以打印沙盒路径之后将沙盒路径复制到Finder前往路径文件夹中,前往应用沙盒。这是一个比较耽误事的方法!幸好有一款叫做simpholders的app,它可以很简单的访问应用的沙盒路径,记得去下载simpholders2哦,第一代iOS8之后就不能用了,app很简单易懂,用下就会了~
现在我们来看看应用沙盒里面这些文件夹都是做什么用的
- Documents :保存应用运行时生成的需要持久化的数据,iTunes同步设备时会备份该目录。例如,游戏应用可将游戏存档保存在该目录
- Library/Caches : 保存应用运行时生成的需要持久化的数据,iTunes同步设备时不会备份该目录。一般存储体积大、不需要备份的非重要数据
- Library/Preference : 保存应用的所有偏好设置,iOS的Settings(设置)应用会在该目录中查找应用的设置信息。iTunes同步设备时会备份该目录
- tmp : 保存应用运行时所需的临时数据,使用完毕后再将相应的文件从该目录删除。应用没有运行时,系统也可能会清除该目录下的文件。iTunes同步设备时不会备份该目录
应用沙盒目录的常见获取方式
沙盒根目录的获取方式
正如上面我们所说:
NSString *home = NSHomeDirectory();
Documents文件夹的获取方式(3种)
第一种( !笨!)
// 利用沙盒根目录拼接字符串
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中保存数据
p3:ASIHTTPRequest本地缓存
相关链接:使用ASIHTTPRequest和ASIDownloadCache实现本地缓存
1、设置全局的Cache
在AppDelegate.h中添加一个全局变量
@interface AppDelegate : UIResponder { ASIDownloadCache *myCache; } @property (strong, nonatomic) UIWindow *window; @property (nonatomic,retain) ASIDownloadCache *myCache;
//自定义缓存 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];
2、设置缓存策略
在实现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框架,更加轻量级和灵活
提供了多线程安全的数据库操作方法,有效地防止数据混乱
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
一、CoreData的简单使用
准备工作
-
创建数据库
- 新建文件,选择
CoreData
->DataModel
- 添加实体(表),
Add Entity
- 给表中添加属性,点击
Attributes
下方的‘+’号
- 新建文件,选择
-
创建模型文件
- 新建文件,选择
CoreData
->NSManaged Object subclass
- 根据提示,选择实体
- 新建文件,选择
-
通过代码,关联数据库和实体
- (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); } }
二、CoreData的表关联
准备工作
-
创建数据库
- 新建文件,选择
CoreData
->DataModel
- 添加实体(表),
Add Entity
, 注意:这里根据关联添加多个实体 - 给表中添加属性,点击
Attributes
下方的‘+’号
- 新建文件,选择
-
创建模型文件
- 新建文件,选择
CoreData
->NSManaged Object subclass
- 根据提示,选择实体,注意:这里先选择被关联的实体,最后添加最上层的实体
- 新建文件,选择
-
通过代码,关联数据库和实体
- (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); } }
-
其他功能与前几种类似,这里不在赘述
三、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); } }
四、多个数据库的使用
注意:
创建多个数据库,即创建多个
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]; }