测试已测试 | ✓ |
Lang语言 | Obj-CObjective C |
许可证 | MIT |
发布最后发布 | 2017年2月 |
由 Nick Donaldson,Michael Gorbach,Justin Kaufman,Alex Rouse,Brian King 维护。
堆栈管理,ActiveRecord 工具,以及 Core Data 的无缝导入。
只需将 Classes/
的内容复制/添加到您的项目中,并确保您链接了 Core Data。不要 复制 Extensions/
的内容。
由于可选的 RZImport 扩展依赖于 RZImport 库,手动安装稍微有些困难。因此,CocoaPods 是推荐安装方法。
使用 RZImport 扩展手动安装
Extensions/
的内容到您的项目中。-DRZV_IMPORT_AVAILABLE=1
如果一切顺利,您的项目应该可以干净地构建,并且 NSManagedObject+RZImport.h
中的方法应该可用。
在 Example
目录中有一个演示项目。该演示项目使用 CocoaPods,可以通过运行以下命令从一个临时目录中打开:
pod try RZVinyl
或者,您可以从根项目目录运行以下命令来配置演示:
cd Example
pod install
然后打开 RZVinylDemo.xcworkspace
并检查演示!
注意:以上步骤假设已安装 CocoaPods gem。
如果您没有安装 CocoaPods,请按照 这里 的说明进行操作。
RZVinyl 完全与 Swift 兼容,并在适当的地方使用可空性注解和轻量级泛型。
要使用RZVinyl,首先将#import "RZVinyl.h"
添加到任何需要使用它的类中,或者添加到应用的.pch
文件中。RZVinyl可以分为三个基本功能区域,如下所示。
RZCoreDataStack
是一个用于构建和管理Core Data堆栈的类,包括模型、管理对象上下文和持久化存储协调器。它提供了一些方便的方法来执行带有一个独立管理对象上下文的并发后台操作,以及一组类方法通过单例模式提供默认堆栈。
// Default configuration, default store URL (in Library directory), no extra options
RZCoreDataStack *myStack = [[RZCoreDataStack alloc] initWithModelName:@"MyModel"
configuration:nil
storeType:NSInMemoryStoreType
storeURL:nil
options:kNilOptions];
[myStack performBlockUsingBackgroundContext:^(NSManagedObjectContext *context) {
// do some stuff with the context
// this block is on a background thread and the context has private-queue confinement
} completion:^(NSError *err) {
// this block is on the main thread
}];
每个管理对象子类都可以提供一个“过时”谓词,将在这里使用以删除所有通过谓词的所有对象。这对于清理过时或孤儿对象很有用。如果你使用带有RZCoreDataStackOptionsEnableAutoStalePurge
选项初始化堆栈,这也可以在每次应用进入后台时调用。
[myStack purgeStaleObjectsWithCompletion:^(NSError *err){
// This is on the main thread
if ( !err ) {
NSLog(@"Purged!");
}
}];
// This context is a child of the disk writer context and a sibling of the main thread context.
// Saving it will automatically merge changes into the main context.
NSManagedObjectContext *backgroundContext = [myStack backgroundManagedObjectContext];
// This context is a child of the primary main thread context.
// It is useful for creating a "sandbox" for temporary edits that may or may not be saved.
// Its objects can safely be used on the main thread (e.g. with UI elements).
// Saving it will automatically push changes up to the primary main thread context.
NSManagedObjectContext *scratchContext = [myStack temporaryManagedObjectContext];
RZVinylRecord
是NSManagedObject
的一个类别,它提供了Active Record模式的部分实现。在NSManagedObject+RZVinylRecord
中的每个方法都有两个签名 - 一个接受一个管理对象上下文参数,另一个使用默认的RZCoreDataStack
的主管理对象上下文。
// Delete all objects of receiver's type in default stack's main context
+ (void)rzv_deleteAll;
// Delete all objects of receiver's type in the provided context
+ (void)rzv_deleteAllInContext:(NSManagedObjectContext *)context;
无上下文版本只能从主线程中使用,否则将抛出异常。同样,如果没有设置默认堆栈,尝试调用这些方法之一而不提供上下文也会抛出异常。
// Inserted into the default main context
MyManagedObject *newObject = [MyManagedObject rzv_newObject];
// Inserted into the provided context
MyManagedObject *newObject = [MyManagedObject rzv_newObjectInContext:context];
这些方法使用实现管理对象子类中的+ (NSString *)rzv_primaryKey;
提供的一个属性来搜索具有该属性的现有对象,如果未找到,则可选地创建一个新对象并使用主键值进行初始化。
// In the default main context
MyManagedObject *existingObjectOrNil = [MyManagedObject rzv_objectWithPrimaryKeyValue:@(12345) createNew:NO];
// In the provided context, creating a new instance if one isn't found
MyManagedObject *existingObjectOrNil = [MyManagedObject rzv_objectWithPrimaryKeyValue:@(12345) createNew:YES inContext:context];
您还可以基于一组其他属性来查找/创建对象。如果createNew
为YES且没有找到匹配项,则将创建一个新实例并用提供的属性字典初始化。
// In the default main context
MyManagedObject *existingObjectOrNil = [MyManagedObject rzv_objectWithAttributes:@{ @"name" : @"Bob Marley" }
createNew:NO];
// In the provided context, creating a new instance if one isn't found
MyManagedObject *existingObjectOrNil = [MyManagedObject rzv_objectWithAttributes:@{ @"name" : @"Bob Marley" }
createNew:YES
inContext:context];
// In the default main context
NSArray *allMyObjects = [MyManagedObject rzv_all];
// In the provided context
NSArray *allMyObjects = [MyManagedObject rzv_allInContext:context];
// In the default main context
NSArray *matchingObjects = [MyManagedObject rzv_where:RZVPred(@"someAttribute >= 18")];
// In the provided context
NSArray *matchingObjects = [MyManagedObject rzv_where:RZVPred(@"someAttribute >= 18") inContext:context];
"where" 方法也有接受排序描述符进行结果排序的版本。
// In the default main context
NSUInteger theCount = [MyManagedObject rzv_count];
// In the provided context, with a predicate
NSUInteger theCount = [MyManagedObject rzv_countWhere:RZVPred(@"someAttribute >= %@", minimum)
inContext:context];
// Uses the object's context
[myObjectInstance rzv_delete];
// In the default main context
[MyManagedObject rzv_deleteAll];
// In the provided context, with a predicate
[MyManagedObject rzv_deleteAllWhere:RZVPred(@"someAttribute == nil") inContext:context];
CORE DATA中保存对象的语义与使用Active Record模式时的期望相差甚远,尤其是在处理更复杂的内容层次结构时,如RZCoreDataStack所示。为了将更改持久化到持久存储,需要保存整个内容树直到其根节点,这将同时保存沿途中任何内容的任何更改。为了避免意外、不明智的后果,通过RZVinylRecord没有提供“保存”方法,保存必须通过管理对象上下文本身来处理。
为此,NSManagedObjectContext+RZVinylSave提供了两种将上下文树保存到持久存储的方法,分为同步和异步两种方式。
NSError *saveError = nil;
if ( ![context rzv_saveToStoreAndWait:&saveError] ) {
NSLog(@"Error saving context: %@", saveError);
}
[context rzv_saveToStoreWithCompletion:^(NSError *error){
// Called on main thread
if ( error ) {
NSLog(@"Error saving context: %@", saveError);
}
}];
RZImport可以与RZVinyl结合使用,以自动从反序列化的JSON或任何其他普通数据NSDictionary
或NSArray
源导入管理对象。
NSManagedObject+RZImport提供了一个部分实现的RZImportable协议,它自动处理对象的唯一性和关系导入。有关工作演示,请参阅示例项目。
要为管理对象子类启用RZImport,创建一个类别并实现来自RZVinylRecord和RZVinylRip非正式协议的以下方法
+ (NSString *)rzv_primaryKey;
实现该方法以返回表示此对象类型“唯一标识符”的属性属性名称
+ (NSString *)rzv_externalPrimaryKey;
如果此对象字典表示中的键与主键属性名称不同,则实现此方法以返回该键。如果不实现,则使用与rzv_primaryKey返回的相同值来查找对象的唯一实例。
您还可以在管理对象类中实现RZImportable的方法来处理验证、提供进一步的自定义键/属性映射等,但有两条重要的注意事项
+ (id)rzi_existingObjectForDict:(NSDictionary *)dict
此方法由 NSManagedObject+RZImport
分类实现以处理 Core Data 并发,并内部调用带有上下文参数的扩展版本
+ (id)rzi_existingObjectForDict:(NSDictionary *)dict inContext:(NSManagedObjectContext *)context;
该分类提供的实现会自动在导入过程中管理唯一对象,通过找到匹配正在导入的字典的现有对象。只要在你的重写实现中总是返回由 super
提供的值,默认实现就是安全的
- (BOOL)rzi_shouldImportValue:(id)value forKey:(NSString *)key
调用 super分类实现处理表示关系的键的递归导入。你可以在子类中重写此方法,只要返回你的重写未处理的键的 super
实现的结果。以下是一个示例。
以下是一个配置为与 RZImport
一起使用的托管对象子类的示例。
RZArtist.h
@interface RZArtist : NSManagedObject
@property (nonatomic, retain) NSNumber *remoteID;
@property (nonatomic, retain) NSDate *birthdate;
@property (nonatomic, retain) NSString *name;
@property (nonatomic, retain) NSNumber *rating;
@property (nonatomic, retain) RZGenre *genre; // one-to-one relationship
@property (nonatomic, retain) NSSet *songs; // one-to-many relationship of 'RZSong' objects
@end
RZArtist+RZImport.h
@interface RZArtist (RZImport) <RZImportable>
@end
RZArtist+RZImport.m
@implementation RZArtist (RZImport)
+ (NSString *)rzv_primaryKey
{
return @"remoteID";
}
+ (NSString *)rzv_externalPrimaryKey
{
return @"id";
}
+ (NSDictionary *)rzi_customMappings
{
return @{ @"dob" : @"birthdate" };
}
+ (NSString *)rzi_dateFormatForKey:(NSString *)key
{
if ( [key isEqualToString:@"dob"] ) {
return @"yyyy-MM-dd";
}
return nil;
}
- (BOOL)rzi_shouldImportValue:(id)value forKey:(NSString *)key
{
// Genre string will be imported as a managed object (RZGenre)
if ( [key isEqualToString:@"genre"] ) {
// Could also use an NSValueTransformer if this will be done in multiple classes
if ( [value isKindOfClass:[NSString class]] ) {
self.genre = [RZGenre rzv_objectWithAttributes:@{ @"name" : value }
createNew:YES
inContext:self.managedObjectContext];
}
return NO;
}
return [super rzi_shouldImportValue:value forKey:key];
}
@end
使用此基本实现,并且假设 RZSong
和 RZGenre
也已经正确配置,你可以做如下操作
// This could just as easily be deserialized JSON
NSDictionary *artistDict = @{
@"id" : @100,
@"dob" : @"1942-11-27", // string -> date, via provided format
@"rating" : @"4.7", // string -> number, automatically
@"name" : @"Jimi Hendrix",
@"genre" : @"Psychedelic Rock", // string -> RZGenre, via protocol method
@"songs" : @[ // array -> RZSong to-many relationship, automatically
@{
@"id" : @1000,
@"title" : @"Hey Joe"
},
@{
@"id" : @1001,
@"title" : @"Spanish Castle Magic"
}
]
};
[[RZCoreDataStack defaultStack] performBlockUsingBackgroundContext:^(NSManagedObjectContext *context) {
// Import jimi and his nested songs from the dictionary
RZArtist *jimi = [RZArtist rzi_objectFromDictionary:artistDict inContext:context];
} completion:^(NSError *err) {
if ( !err ) {
[myStack save:YES];
}
// Fetch the record from the main thread
RZArtist *mainThreadJimi = [RZArtist rzv_objectWithPrimaryKeyValue:@100];
}];
有关更全面的文档,请参阅 CococaDocs 页面。
RZVinyl 依照 MIT 许可证授权。详情请参阅 LICENSE
文件。