最近做了下CoreData的增删改查,简单谈谈封装的过程。可结合demo的提交记录阅读。
主控制器,里面有7个点击事件,然后加上对一个表的简单增删改查,也就200多行。
#import "CoreDataMainViewController.h"
#import
#import "User+CoreDataProperties.h"
#define db_user @"User"
@interface CoreDataMainViewController ()
@property (nonatomic,strong) NSManagedObjectContext * managedObjectContext;
@end
@implementation CoreDataMainViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
#pragma mark - action
- (IBAction)insertAction:(id)sender {
[self insertWithUser:@{@"name":@"zhangsan",@"userId":@"1"}];
}
- (IBAction)insertBatchAction:(id)sender {
NSArray * params = @[@{@"name":@"zhangsan",@"userId":@"1"},
@{@"name":@"lisi",@"userId":@"2"}
];
[self insertWithUserArr:params];
}
- (IBAction)delAction:(id)sender {
[self deleteWithID:@"1"];
}
- (IBAction)delBatchAction:(id)sender {
[self deleteAll];
}
- (IBAction)searchAction:(id)sender {
[self searchWithID:@"2"];
}
- (IBAction)searchAllAction:(id)sender {
[self searchAll];
}
- (IBAction)modityAction:(id)sender {
[self updateWithID:@"1" params:@{@"name":@"wangwu"}];
}
#pragma mark - CRUD
- (void)insertWithUser:(NSDictionary *)param{
User *user = [NSEntityDescription insertNewObjectForEntityForName:db_user inManagedObjectContext:self.managedObjectContext];
user.userId = param[@"userId"];
user.name = param[@"name"];
[self saveContext:self.managedObjectContext];
}
- (void)insertWithUserArr:(NSArray *)arr{
for (NSDictionary * param in arr) {
User *user = [NSEntityDescription insertNewObjectForEntityForName:db_user inManagedObjectContext:self.managedObjectContext];
user.userId = param[@"userId"];
user.name = param[@"name"];
}
[self saveContext:self.managedObjectContext];
}
- (void)deleteWithID:(NSString *)idString{
NSManagedObjectContext *context = self.managedObjectContext;
// 2.创建查询请求
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:db_user];
NSPredicate *predicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:@"userId == %@", idString]];
[fetchRequest setPredicate:predicate];
// 3.执行查询方法,返回结果
NSError *error = nil;
NSArray *resultArray = [context executeFetchRequest:fetchRequest error:&error];
if (error) {
return;
}
// 4.将MO标记为删除
for (User *user in resultArray) {
[context deleteObject:user];
}
// 5.提交修改
[self saveContext:self.managedObjectContext];
}
/// 删除所有记录
- (void)deleteAll {
// 1.获取MOC
NSManagedObjectContext *context = self.managedObjectContext;
// 2.创建查询请求
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:db_user];
// 3.执行查询方法,返回结果
NSError *error = nil;
NSArray *resultArray = [context executeFetchRequest:fetchRequest error:&error];
if (error) {
return;
}
// 4.将MO标记为删除
for (User *user in resultArray) {
[context deleteObject:user];
}
// 5.提交修改
[self saveContext:context];
}
/// 查询指定记录
- (NSArray *)searchWithID:(NSString *)idString {
// 1.获取MOC
NSManagedObjectContext *context = self.managedObjectContext;
// 2.创建查询请求
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:db_user];
NSPredicate *predicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:@"userId == %@", idString]];
[fetchRequest setPredicate:predicate];
// 3.执行查询方法,返回结果
NSError *error = nil;
NSArray *resultArray = [context executeFetchRequest:fetchRequest error:&error];
[self printSearchResult:resultArray];
return resultArray;
}
/// 查询所有记录
- (NSArray *)searchAll{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:db_user];
NSError *error = nil;
NSArray *resultArray = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
[self printSearchResult:resultArray];
return resultArray;
}
- (void)printSearchResult:(NSArray *)resultArray{
NSLog(@"查询结果%lu条",(unsigned long)resultArray.count);
for (User *user in resultArray) {
NSLog(@"userId=%@,name=%@", user.userId, user.name);
}
}
- (void)saveContext:(NSManagedObjectContext *)context {
if (!context) {
return;
}
NSError *error = nil;
// 判断MOC监听的MO对象是否有改变,如果有则提交保存
if ([context hasChanges] && ![context save:&error]) {
// log error
NSAssert(YES, @"save error!!!");
}
if (context.parentContext) {
// 递归保存
[self saveContext:context.parentContext];
}
}
/// 更新指定记录
- (void)updateWithID:(NSString *)idString params:(NSDictionary *)params{
// 1.获取MOC
NSManagedObjectContext *context = self.managedObjectContext;
// 2.创建查询请求
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:db_user];
NSPredicate *predicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:@"userId == %@", idString]];
[fetchRequest setPredicate:predicate];
// 3.执行查询方法,返回结果
NSError *error = nil;
NSArray *resultArray = [context executeFetchRequest:fetchRequest error:&error];
if (error) {
return;
}
// 4.更新MO
for (User *user in resultArray) {
user.name = params[@"name"];
}
// 5.提交修改
[self saveContext:context];
}
#pragma mark - context
- (NSManagedObjectContext *)managedObjectContext{
if (!_managedObjectContext) {
_managedObjectContext = [self getContext];
}
return _managedObjectContext;
}
- (NSManagedObjectContext *)getContext {
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"Tesl" withExtension:@"momd"];
//根据模型文件创建模型对象
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
//2、创建持久化助理
//利用模型对象创建助理对象
NSPersistentStoreCoordinator *store = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
//数据库的名称和路径
NSString *docStr = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *sqlPath = [docStr stringByAppendingPathComponent:@"Tesl.sqlite"];
NSLog(@"path = %@", sqlPath);
NSURL *sqlUrl = [NSURL fileURLWithPath:sqlPath];
//设置数据库相关信息
[store addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:sqlUrl options:nil error:nil];
//3、创建上下文
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
//关联持久化助理
[context setPersistentStoreCoordinator:store];
return context;
}
@end
由于CoreData的增删改查需要手动管理多线程,然后考虑引用MagicalRecordCRUD来管理。再在CoreDataMainViewController增加MR的增删改查代码,明显整个类都臃肿了起来,而且同样功能的放在一起,你肯定也屡不清,我把CoreData的增删改查提取出来了,我做了以下步骤:
第一步:新建了协议类CRUDProtocol.h
@protocol CRUDProtocol
- (void)insertWithUser:(NSDictionary *)param;
- (void)insertWithUserArr:(NSArray *)arr;
- (void)deleteWithID:(NSString *)idString;
- (void)deleteAll;
- (NSArray *)searchWithID:(NSString *)idString;
- (NSArray *)searchAll;
- (void)updateWithID:(NSString *)idString params:(NSDictionary *)params;
@end
第二步:新建CRUD类,实现CRUDProtocol协议
@interface CRUD : NSObject
@end
#import "CRUD.h"
#import
#import "User+CoreDataProperties.h"
@interface CRUD()
@property (nonatomic,strong) NSManagedObjectContext * managedObjectContext;
@end
@implementation CRUD
- (void)insertWithUser:(NSDictionary *)param{
NSString * userId = param[db_user_userId];
NSArray * searchResultArr = [self searchWithID:userId];
if (searchResultArr.count > 0) {
[self updateWithID:userId params:param];
return;
}else{
User *user = [NSEntityDescription insertNewObjectForEntityForName:db_user inManagedObjectContext:self.managedObjectContext];
user.userId = userId;
user.name = param[db_user_name];
[self saveContext:self.managedObjectContext];
}
}
- (void)insertWithUserArr:(NSArray *)arr{
for (NSDictionary * param in arr) {
[self insertWithUser:param];
}
[self saveContext:self.managedObjectContext];
}
- (void)deleteWithID:(NSString *)idString{
NSManagedObjectContext *context = self.managedObjectContext;
// 2.创建查询请求
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:db_user];
NSPredicate *predicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:@"%@ == %@",db_user_userId ,idString]];
[fetchRequest setPredicate:predicate];
// 3.执行查询方法,返回结果
NSError *error = nil;
NSArray *resultArray = [context executeFetchRequest:fetchRequest error:&error];
if (error) {
return;
}
// 4.将MO标记为删除
for (User *user in resultArray) {
[context deleteObject:user];
}
// 5.提交修改
[self saveContext:self.managedObjectContext];
}
/// 删除所有记录
- (void)deleteAll {
// 1.获取MOC
NSManagedObjectContext *context = self.managedObjectContext;
// 2.创建查询请求
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:db_user];
// 3.执行查询方法,返回结果
NSError *error = nil;
NSArray *resultArray = [context executeFetchRequest:fetchRequest error:&error];
if (error) {
return;
}
// 4.将MO标记为删除
for (User *user in resultArray) {
[context deleteObject:user];
}
// 5.提交修改
[self saveContext:context];
}
/// 查询指定记录
- (NSArray *)searchWithID:(NSString *)idString {
// 1.获取MOC
NSManagedObjectContext *context = self.managedObjectContext;
// 2.创建查询请求
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:db_user];
NSPredicate *predicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:@"%@ == %@",db_user_userId ,idString]];
[fetchRequest setPredicate:predicate];
// 3.执行查询方法,返回结果
NSError *error = nil;
NSArray *resultArray = [context executeFetchRequest:fetchRequest error:&error];
[self printSearchResult:resultArray];
return resultArray;
}
/// 查询所有记录
- (NSArray *)searchAll{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:db_user];
NSError *error = nil;
NSArray *resultArray = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
[self printSearchResult:resultArray];
return resultArray;
}
- (void)printSearchResult:(NSArray *)resultArray{
NSLog(@"查询结果%lu条",(unsigned long)resultArray.count);
for (User *user in resultArray) {
NSLog(@"userId=%@,name=%@", user.userId, user.name);
}
}
- (void)saveContext:(NSManagedObjectContext *)context {
if (!context) {
return;
}
NSError *error = nil;
// 判断MOC监听的MO对象是否有改变,如果有则提交保存
if ([context hasChanges] && ![context save:&error]) {
// log error
NSAssert(YES, @"save error!!!");
}
if (context.parentContext) {
// 递归保存
[self saveContext:context.parentContext];
}
}
/// 更新指定记录
- (void)updateWithID:(NSString *)idString params:(NSDictionary *)params{
// 1.获取MOC
NSManagedObjectContext *context = self.managedObjectContext;
// 2.创建查询请求
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:db_user];
NSPredicate *predicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:@"%@ == %@",db_user_userId ,idString]];
[fetchRequest setPredicate:predicate];
// 3.执行查询方法,返回结果
NSError *error = nil;
NSArray *resultArray = [context executeFetchRequest:fetchRequest error:&error];
if (error) {
return;
}
// 4.更新MO
for (User *user in resultArray) {
user.name = params[db_user_name];
}
// 5.提交修改
[self saveContext:context];
}
#pragma mark - getter
- (NSManagedObjectContext *)managedObjectContext{
if (!_managedObjectContext) {
_managedObjectContext = [self getContext];
}
return _managedObjectContext;
}
- (NSManagedObjectContext *)getContext {
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"Tesl" withExtension:@"momd"];
//根据模型文件创建模型对象
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
//2、创建持久化助理
//利用模型对象创建助理对象
NSPersistentStoreCoordinator *store = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
//数据库的名称和路径
NSString *docStr = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *sqlPath = [docStr stringByAppendingPathComponent:@"Tesl.sqlite"];
NSLog(@"path = %@", sqlPath);
NSURL *sqlUrl = [NSURL fileURLWithPath:sqlPath];
//设置数据库相关信息
[store addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:sqlUrl options:nil error:nil];
//3、创建上下文
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
//关联持久化助理
[context setPersistentStoreCoordinator:store];
return context;
}
@end
第三步:在CoreDataMainViewController中调用
#import "CoreDataMainViewController.h"
#import "CRUD.h"
@interface CoreDataMainViewController ()
@property (nonatomic,strong) CRUD * crud;
@end
@implementation CoreDataMainViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
#pragma mark - action
- (IBAction)insertAction:(id)sender {
[self.crud insertWithUser:@{db_user_name:@"zhangsan",db_user_userId:@"1"}];
}
- (IBAction)insertBatchAction:(id)sender {
NSArray * params = @[@{db_user_name:@"zhangsan",db_user_userId:@"1"},
@{db_user_name:@"lisi",db_user_userId:@"2"}
];
[self.crud insertWithUserArr:params];
}
- (IBAction)delAction:(id)sender {
[self.crud deleteWithID:@"1"];
}
- (IBAction)delBatchAction:(id)sender {
[self.crud deleteAll];
}
- (IBAction)searchAction:(id)sender {
[self.crud searchWithID:@"2"];
}
- (IBAction)searchAllAction:(id)sender {
[self.crud searchAll];
}
- (IBAction)modityAction:(id)sender {
[self.crud updateWithID:@"1" params:@{db_user_name:@"wangwu"}];
}
@end
在CoreDataMainViewController只有几个点击事件和几个方法的调用,加起来也就50几行。回到刚才的问题,我需要把CoreData的增删改查改成MR的增删改查。
我做了以下步骤:
1.引入第三方库pod 'MagicalRecord', '~> 2.3.2'
2.新建MagicalRecordCRUD,然后实现CRUDProtocol协议
#import
#import "CRUDProtocol.h"
#import "CRUDHeader.h"
NS_ASSUME_NONNULL_BEGIN
@interface MagicalRecordCRUD : NSObject
@end
NS_ASSUME_NONNULL_END
#import "MagicalRecordCRUD.h"
#import
#import "User+CoreDataClass.h"
@implementation MagicalRecordCRUD
- (void)insertWithUser:(NSDictionary *)param{
NSString *userID = param[db_user_userId];
NSString *userName = param[db_user_name];
// localContext -> rootSavingContext
[MagicalRecord saveWithBlock:^(NSManagedObjectContext * _Nonnull localContext) {
User *user = [NSEntityDescription insertNewObjectForEntityForName:db_user inManagedObjectContext:localContext];
user.userId = userID;
user.name = userName;
} completion:^(BOOL contextDidSave, NSError * _Nullable error) {
}];
}
- (void)insertWithUserArr:(NSArray *)arr{
}
- (NSArray *)searchAll{
NSArray *resultArray = [User MR_findAll];
return resultArray;
}
- (NSArray *)searchWithID:(NSString *)idString{
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%@ == %@",db_user_userId, idString];
NSArray *resultArray = [User MR_findAllWithPredicate:predicate];
return resultArray;
}
- (void)updateWithID:(NSString *)idString params:(NSDictionary *)params{
[MagicalRecord saveWithBlock:^(NSManagedObjectContext * _Nonnull localContext) {
NSPredicate *predicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:@"%@ == %@",db_user_userId, idString]];
NSArray *resultArray = [User MR_findAllWithPredicate:predicate inContext:localContext];
for (User *user in resultArray) {
user.name = params[db_user_name];
}
} completion:^(BOOL contextDidSave, NSError * _Nullable error) {
}];
}
- (void)deleteWithID:(NSString *)idString{
[MagicalRecord saveWithBlock:^(NSManagedObjectContext * _Nonnull localContext) {
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%@ == %@",db_user_userId, idString];
NSArray *resultArray = [User MR_findAllWithPredicate:predicate inContext:localContext];
[localContext MR_deleteObjects:resultArray];
} completion:^(BOOL contextDidSave, NSError * _Nullable error) {
}];
}
- (void)deleteAll{
[MagicalRecord saveWithBlock:^(NSManagedObjectContext * _Nonnull localContext) {
NSArray *resultArray = [User MR_findAllInContext:localContext];
[localContext MR_deleteObjects:resultArray];
}];
}
@end
3.在CoreDataMainViewController中的调用
我仅需要导入MagicalRecordCRUD并且替换CRUD,就实现了数据库的替换。
#import "CoreDataMainViewController.h"
#import "MagicalRecordCRUD.h"
@interface CoreDataMainViewController ()
@property (nonatomic,strong) MagicalRecordCRUD * crud;
@end
@implementation CoreDataMainViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
#pragma mark - action
- (IBAction)insertAction:(id)sender {
[self.crud insertWithUser:@{db_user_name:@"zhangsan",db_user_userId:@"1"}];
}
- (IBAction)insertBatchAction:(id)sender {
NSArray * params = @[@{db_user_name:@"zhangsan",db_user_userId:@"1"},
@{db_user_name:@"lisi",db_user_userId:@"2"}
];
[self.crud insertWithUserArr:params];
}
- (IBAction)delAction:(id)sender {
[self.crud deleteWithID:@"1"];
}
- (IBAction)delBatchAction:(id)sender {
[self.crud deleteAll];
}
- (IBAction)searchAction:(id)sender {
[self.crud searchWithID:@"2"];
}
- (IBAction)searchAllAction:(id)sender {
[self.crud searchAll];
}
- (IBAction)modityAction:(id)sender {
[self.crud updateWithID:@"1" params:@{db_user_name:@"wangwu"}];
}
- (MagicalRecordCRUD *)crud{
if (!_crud) {
_crud = [MagicalRecordCRUD new];
}
return _crud;
}
@end
@property (nonatomic,strong) MagicalRecordCRUD * crud;
这个地方我是用类实现的,其实MagicalRecordCRUD和CRUD都实现了相关协议,可以改成@property (nonatomic,strong) id
这样的话,我仅需要替换初始化就能实现数据库的替换:
- (id)crud{
if (!_crud) {
_crud = [MagicalRecordCRUD new];
}
return _crud;
}
总结:
1.我这篇文章属于抛砖引玉,真正想实现好的封装,经验和阅历少不了。最好能熟练使用各种设计模式,我这里推荐<<设计模式之禅>>。通俗易懂,书中每种设计模式都有例子说明。
2.如果是增加多表查询,又会怎么封装呢?其实我的代码后面有实现一个一对多的,如果两个类有共性,是不是考虑继承一个父类,共用的部分提取到父类。如果子类在共用的方法上有差异,是不是可以考虑模板模式来实现子类的刷新。
3.对于多个类似的功能,如阅读解析时可能有txt,html,epub等多种格式,可考虑策略模式替换。我这只是一个引入,封装是有目的性的,是变化的,不是一开始就考虑项目有多大,封装有多好,扩展性有多强。随着需求的变化,代码相应的也要产生变化。
4.在一个小类中,不需要任何封装;但类大了,任何一个变量都是灾难性的。例如一个阅读模块已有5000多行,让你加个弹窗,你能把变量直接写在阅读类里吗?可想阅读是多么的难易维护。这个时候对弹窗的封装是多么的有必要。
5.我经常听到身边的同事说,少用继承。如果你对代码封装很了解,也许说出这话来,理解的很深刻;如果你只是听别人说或网上看到的,知其然不知其所以然,这话给你的误解太深了。既然语言支持继承,为什么我们不用呢?当然要恰当运用,不要乱用。如果一个工程里都继承自BaseViewController,里面有很多代码,那么这是灾难性的,BaseViewController有谁敢去修改呢。
6.有任何问题欢迎留言交流。
其他封装文章:
iOS 模板模式封装shell 解压、拷贝、删除命令
iOS责任链设计模式实现逐个弹窗