您的位置:首页 > 移动开发 > IOS开发

ios10.3之CoreData的详细教程

2017-07-31 22:07 351 查看
首先如果要使用CoreData可以选择在初创项目时选择添加coredata,也可以选择自己添加coredata文件


系统添加coredata后会在Appdelegate类中自动添加一个persistentContainer属性,和一个saveContext方法。在.m中还为persistentContainer写了getter

- (NSPersistentContainer *)persistentContainer {
// The persistent container for the application. This implementation creates and returns a container, having loaded the store for the application to it.
@synchronized (self) {
if (_persistentContainer == nil) {
_persistentContainer = [[NSPersistentContainer alloc] initWithName:@"XWMyCoreDataDemo"];
[_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error) {
if (error != nil) {
NSLog(@"Unresolved error %@, %@", error, error.userInfo);
abort();
}
}];
}
}

return _persistentContainer;
}
- (void)saveContext {
NSManagedObjectContext *context = self.persistentContainer.viewContext;
NSError *error = nil;
if ([context hasChanges] && ![context save:&error]) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(@"Unresolved error %@, %@", error, error.userInfo);
abort();
}
}
需要特别注意的是两个方法实现中都有abort()函数,这个函数会造成程序崩溃,如果是开发模式,可以利于调试,如果是已上架模式最好移除这个函数,以免影响用户体验。

然后可以找到.xcdatamodeld文件,开始添加模型,在我的demo中添加了Person和Card两个模型,详情如下




Person中添加了两个属性name和age,类型分别为String和int32_t,card中也添加了两个属性color和size,类型均为NSObject(截图时我想尝试下Decimal数据类型尝试了下,后来改成Transformable,即NSObject类型)

需要注意的是一般我们都会选择手动生成继承自NSManagedObject的Person和Card的类方便我们查看等,而.xcdatamodeld文件在编译时会自动生成同样的Person和Card类,所以我们可以选择禁用模型的自动生成功能,如下图所示,我们可以选择模型,然后找到Class中Codegen将其改成Manul/None,即可禁止其自动生成模型子类。



然后我们还可以在文件中设置两个模型的关系,如下图所示,我们可以点击relationship中的+,添加关系,


需要注意的是添加单相关系的时候inverse是不会有值的,只有双向关系,或者有其他模型引用这个模型时才会有值。

其中relationship中的值即为生成类时系统会自动生成的属性,可以看到选择属性时右方可以选择ToOne或者ToMany,即一个Person模型可以对应几个Card,如果选择一对多

那么系统会自动生成一个集合属性,并且会生成相关的添加值的方法。然后我们可以按照下图选择生成Person和Card的类文件。


生成后项目文件如下所示

#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>

@class Card;

NS_ASSUME_NONNULL_BEGIN

@interface Person : NSManagedObject

@end

NS_ASSUME_NONNULL_END

#import "Person+CoreDataProperties.h"
 
#import "Person+CoreDataClass.h"
#import "Card+CoreDataClass.h"

@implementation Person

@end
 
#import "Person+CoreDataClass.h"

NS_ASSUME_NONNULL_BEGIN

@interface Person (CoreDataProperties)

+ (NSFetchRequest<Person *> *)fetchRequest;

@property (nullable, nonatomic, copy) NSString *name;
@property (nonatomic) int32_t age;
@property (nullable, nonatomic, retain) NSSet<Card *> *cards;

@end
 

@interface Person (CoreDataGeneratedAccessors)
//用于添加属性值的方法
- (void)addCardsObject:(Card *)value;
- (void)removeCardsObject:(Card *)value;
- (void)addCards:(NSSet<Card *> *)values;
- (void)removeCards:(NSSet<Card *> *)values;

@end

NS_ASSUME_NONNULL_END
#import "Person+CoreDataProperties.h"
@implementation Person (CoreDataProperties)

+ (NSFetchRequest<Person *> *)fetchRequest {
return [[NSFetchRequest alloc] initWithEntityName:@"Person"];
}

@dynamic name;
@dynamic age;
@dynamic cards;

@end
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>

@class NSObject, Person;

NS_ASSUME_NONNULL_BEGIN

@interface Card : NSManagedObject

@end

NS_ASSUME_NONNULL_END

#import "Card+CoreDataProperties.h"
#import "Card+CoreDataClass.h"
#import "Person+CoreDataClass.h"

@implementation Card

@end 
#import "Card+CoreDataClass.h"

NS_ASSUME_NONNULL_BEGIN

@interface Card (CoreDataProperties)

+ (NSFetchRequest<Card *> *)fetchRequest;

@property (nullable, nonatomic, retain) NSObject *color;
@property (nullable, nonatomic, copy) NSObject *size;
@property (nullable, nonatomic, retain) Person *owner;

@end 
NS_ASSUME_NONNULL_END
#import "Card+CoreDataProperties.h"

@implementation Card (CoreDataProperties)

+ (NSFetchRequest<Card *> *)fetchRequest {
return [[NSFetchRequest alloc] initWithEntityName:@"Card"];
}

@dynamic color;
@dynamic size;
@dynamic owner;

@end
可以发现模型生成的类都是继承自NSManagedObject,且相关属性都使用了@dynamic,也就是系统不会自动生成getter和setter,且在使用的过程中可以发现如果使用alloc初始化模型的时候是会报错的!

最后附上使用过程

#import "ViewController.h"
#import "Card+CoreDataClass.h"
#import "Person+CoreDataClass.h"
#import "AppDelegate.h"
@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];
AppDelegate *delegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
NSManagedObjectContext *context = delegate.persistentContainer.viewContext; //context常用
//创建一个Person模型对象 模型对象初始化必须有一个NSEntityDescription
Person *p = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:context];
p.name = @"Will";
p.age = 24;
//创建一个Card模型对象
Card *card = [NSEntityDescription insertNewObjectForEntityForName:@"Card" inManagedObjectContext:context];
card.color = [UIColor redColor];
card.size = NSStringFromCGSize(CGSizeMake(70, 100));
[p addCardsObject:card];
NSError *error = nil;
[context save:&error];	//保存操作
if (error) {
NSLog(@"%@",error.userInfo);
}
/**    下面是查询操作,可以删除上面代码,重新运行       */
//    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Person"];	//初始化一个Person的查询请求
NSFetchRequest *request = [Person fetchRequest];	//系统自动生成了获取查询请求的方法
NSSortDescriptor *descriptor = [NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES];//为请求设置排序实例
request.sortDescriptors = @[descriptor];

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name like '?il?'"];//为请求设置过滤条件
request.predicate = predicate;
NSArray *array = [context executeFetchRequest:request error:&error]; //根据请求执行查找,结果存于一个数组。
for (Person *p in array) {
NSLog(@"%@,%d,%@",p.name,p.age,p.cards);
}

}

- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

@end
如果对sqlite熟悉的话,可以发现查找的工作很熟悉,就相当于数据库执行"select * from Person order by name where name like '?il?'"的操作,以前系统为我们提供的context查找操作是同步的(现在默认的查找操作默认的是异步的),所以当时如果查询操作是很耗时的话,会影响性能,当时的解决办法是进行异步查询操作
NSAsynchronousFetchRequest *request = [[NSAsynchronousFetchRequest alloc]initWithFetchRequest:[Person fetchRequest] completionBlock:^(NSAsynchronousFetchResult * _Nonnull result) {
if (result) {
NSLog(@"%@",result.finalResult);
NSLog(@"%@",[NSThread currentThread]);
NSLog(@"%@",@(context.concurrencyType));
}
}];
[context executeRequest:request error:&error];
但是不管是同步还是异步的查询操作都是在主线程进行的,如果想要放在子线程的话需要更改context的concurrencyType,因为concurrencyType是readonly,且没有找到相关设置方法,所以目前我采用的重新构建一个context。
NSManagedObjectContext *backContext = [[NSManagedObjectContext alloc]initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[backContext setPersistentStoreCoordinator:context.persistentStoreCoordinator];

NSAsynchronousFetchRequest *request = [[NSAsynchronousFetchRequest alloc]initWithFetchRequest:[Person fetchRequest] completionBlock:^(NSAsynchronousFetchResult * _Nonnull result) {
if (result) {
NSLog(@"%@",result.finalResult);
NSLog(@"%@",[NSThread currentThread]);
NSLog(@"%@",@(context.concurrencyType));
}
}];
[backContext executeRequest:request error:&error];
如果想要删除存储的模型的话
NSFetchRequest *request = [Person fetchRequest];
NSArray *array = [context executeFetchRequest:request error:&error];
for (Person *p in array) {
if ([p.name isEqualToString:@"will"]) {
[context deleteObject:p];
[context save:&error];
}
}
很重要的一点是,每次进行操作后需要保存才能生效!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息