Vokoder
一个轻量级的 Core Data 栈,具有高效导入和导出的功能。
安装
Vokoder 通过 CocoaPods 提供。要安装它,只需将以下行添加到您的 Podfile 中:
pod "Vokoder"
如果打算在 Swift 中使用 Vokoder,请使用 Swift
子规格。
pod "Vokoder/Swift"
Vokoder 需要 iOS 7、tvOS 9 或 macOS 10.9。Swift 扩展需要 Swift 3 和 Xcode 8。
子规格
大部分功能都在 Core
子规格中。如果您没有使用任何数据源,您可以只包含 Core
子规格。
在 DataSources
子规格中有用于支持各种视图与 Core Data 数据的数据源,它进一步细分为
FetchedResults
包含一个基于获取结果的控制器的基本数据源,旨在与UITableView
一起使用。PagingFetchedResults
基于FetchedResults
,但支持分页加载。Collection
基于FetchedResults
,但旨在与UICollectionView
一起使用。
可选的 Swift
子规格包括一些用于强类型和更清洁语法的 Swift 扩展。如果您打算在 Swift 中使用 Vokoder,建议使用此子规格。此子规格包括所有其他子规格。
MapperMacros
子规约中包含了用于导入和导出创建托管对象属性映射的宏。此子规约默认包含,但由于宏仅从Objective-C代码可用,因此从Swift
子规约中排除。
使用方法
设置数据模型
// Omit the .xcdatamodeld extension on the model file name
// Save to disk
[[VOKCoreDataManager sharedInstance] setResource:@"VOKCoreDataModel"
database:@"VOKCoreDataModel.sqlite"];
// Or, create an in-memory store:
[[VOKCoreDataManager sharedInstance] setResource:@"VOKCoreDataModel"
database:nil];
使用Vokoder的Mapper
Vokoder提供了一种轻量级的映射器,用于将Foundation对象导入Core Data。一旦设置了映射,可以轻松地导入以字典形式表示的数组数组。如果没有提供映射,Vokoder将使用其默认映射。默认映射假设外键名称与您的Core Data属性相同。它将尽力识别日期和数字。
建议您设置自己的映射。提供宏使其变得有趣且简单。以下是为托管对象子类VOKPerson
设置映射的示例。由于映射器不会在应用程序启动之间持久化,因此请确保在每次应用程序启动时设置您的映射。请注意,此示例使用了VOKKeyForInstanceOf
宏,它来自VOKUtilities/VOKKeyPathHelper,这是Vokoder的依赖项。
// A date formatter will enable Vokoder to turn strings into NSDates
NSDateFormatter *dateFormatter = [NSDateFormatter someCustomDateFormatter];
// A number formatter will do the same, turning strings into NSNumbers
NSNumberFormatter *numberFormatter = [NSNumberFormatter new];
NSArray *maps = @[
VOKMapForeignToLocalClassProperty(@"ticket_number", VOKPerson, ticketNumber), //the first argument is the foreign key,
VOKMapForeignToLocalClassProperty(@"first_name", VOKPerson, firstName), //second argument is the class, and then local property
VOKMapForeignToLocalClassProperty(@"last_name", VOKPerson, lastName),
VOKMapForeignToLocalClassProperty(@"ss_num", VOKPerson, socialSecurityNumber),
[VOKManagedObjectMap mapWithForeignKeyPath:@"salary"
coreDataKey:VOKKeyForInstanceOf(VOKPerson, salary)
numberFormatter:numberFormatter],
[VOKManagedObjectMap mapWithForeignKeyPath:@"dob"
coreDataKey:VOKKeyForInstanceOf(VOKPerson, dateOfBirth)
dateFormatter:dateFormatter],
];
// The VOKKeyForInstanceOf(...) macro will prevent you from specifying a property that does not exist on a specific class.
// The unique key is an NSString to uniquely identify local entities. If nil, each import can create duplicate objects.
VOKManagedObjectMapper *mapper = [VOKManagedObjectMapper mapperWithUniqueKey:VOKKeyForInstanceOf(VOKPerson, ticketNumber)
andMaps:maps];
// By default, missing parameters and null parameters in the import data will nil out an attribute's value.
// With ignoreNullValueOverwrites set to YES, the maps will leave existing attributes alone unless new data is provided.
mapper.ignoreNullValueOverwrites = YES;
// By default, Vokoder will complain about every single parameter that can't be set.
// With ignoreOptionalNullValues set to YES, Vokoder will not warn about mismatched classes or null/nil values.
mapper.ignoreOptionalNullValues = YES;
// Set the mapper and Vokoder will handle the rest.
[[VOKCoreDataManager sharedInstance] setObjectMapper:mapper
forClass:[VOKPerson class]];
一旦映射器设置好了,Vokoder可以将Foundation对象转换为托管对象,然后再将其转换回Foundation对象。
VOKMappableModel
Vokoder包括了VOKMappableModel
协议,该协议为模型类指定了如何进行映射的结构。任何声明自己符合VOKMappableModel
的类将自动根据协议方法创建映射器,并注册到VOKCoreDataManager
的共享实例。
VOKMappableModel
协议需要实现+ (NSString *)uniqueKey
和+ (NSArray *)coreDataMaps
,它们应返回上述部分中示例中传递给[VOKManagedObjectMapper mapperWithUniqueKey:andMaps:]
的两个参数。可选地,可以实现+ (BOOL)ignoreNullValueOverwrites
、+ (BOOL)ignoreOptionalNullValues
和+ (VOKPostImportBlock)importCompletionBlock
,以设置映射器上的忽略值或设置后导入块。
在上一节示例中构建的映射器可以通过使其遵守VOKMappableModel
,包含在SomeManagedObjectSubclass
中。
@interface SomeManagedObjectSubclass : NSManagedObject <VOKMappableModel>
// …properties and such
@end
@implementation SomeManagedObjectSubclass
// …other methods and such
#pragma mark - VOKMappableModel
+ (NSArray *)coreDataMaps
{
// A date formatter will enable Vokoder to turn strings into NSDates
NSDateFormatter *dateFormatter = [NSDateFormatter someCustomDateFormatter];
// A number formatter will do the same, turning strings into NSNumbers
NSNumberFormatter *numberFormatter = [NSNumberFormatter new];
return = @[
VOKMapForeignToLocalForSelf(@"ticket_number", ticketNumber),
VOKMapForeignToLocalForSelf(@"first_name", firstName),
VOKMapForeignToLocalForSelf(@"last_name", lastName),
VOKMapForeignToLocalForSelf(@"ss_num", socialSecurityNumber),
[VOKManagedObjectMap mapWithForeignKeyPath:@"salary"
coreDataKey:VOKKeyForSelf(salary)
numberFormatter:numberFormatter],
[VOKManagedObjectMap mapWithForeignKeyPath:@"dob"
coreDataKey:VOKKeyForSelf(dateOfBirth)
dateFormatter:dateFormatter],
];
}
+ (NSString *)uniqueKey
{
// The VOKKeyForSelf(...) macro will prevent you from specifying a property that does not exist on the current class.
// The unique key is an NSString to uniquely identify local entities. If nil each import can create duplicate objects.
return VOKKeyForSelf(ticketNumber);
}
+ (BOOL)ignoreNullValueOverwrites
{
// By default, missing parameters and null parameters in the import data will nil out an attribute's value.
// With ignoreNullValueOverwrites set to YES, the maps will leave set attributes alone unless new data is provided.
return YES;
}
+ (BOOL)ignoreOptionalNullValues
{
// By default Vokoder will complain about every single parameter that can't be set
// With ignoreOptionalNullValues set to YES Vokoder will not warn about mismatched classes or null/nil values
return YES;
}
…
@end
安全导入
Vokoder提供了许多方法将数据导入Core Data。最简单、最易于使用的方法是通过VOKManagedObjectAdditions
类别提供。给定一个字典数组,Vokoder将在临时上下文中创建或更新托管对象,然后通过主队列上的完成块安全地从主上下文返回托管对象。
[SomeManagedObjectSubclass vok_addWithArrayInBackground:importArray
completion:^(NSArray *arrayOfManagedObjects) {
// This completion block runs on the main queue
SomeManagedObjectSubclass *obj = arrayOfManagedObjects.firstObject;
}];
为了对后台操作有更多控制,VOKCoreDataManager类提供了更多通用的方法。Vokoder将处理队列并提供临时上下文,无需自动导入或返回任何内容。
+ (void)writeToTemporaryContext:(VOKWriteBlock)writeBlock completion:(void (^)(void))completion;
最后,对于那些想要完全控制的人,你可以创建自己的临时上下文,每个上下文都有自己的串行后台队列。只要你在后台操作中使用临时上下文,Vokoder就会让你走自己的路。
NSManagedObjectContext *backgroundContext = [[VOKCoreDataManager sharedInstance] temporaryContext];
[backgroundContext performBlock:^{
SomeManagedObjectSubclass *thing = [SomeManagedObjectSubclass vok_newInstanceWithContext:backgroundContext];
thing.someArbitrayAttribute = @"hello";
[[VOKCoreDataManager sharedInstance] saveAndMergeWithMainContext:backgroundContext];
}];
注意:手动创建或通过便利后台方法提供的临时上下文是主上下文的子上下文。
插入记录
VOKPerson *person = [VOKPerson vok_newInstance];
person.firstName = @"Rohan";
person.lastName = @"Panchal";
[[VOKCoreDataManager sharedInstance] saveMainContextAndWait];
查询记录
与上面的示例类似,这个示例使用了VOKUtilities/VOKKeyPathHelper中的VOKKeyForInstanceOf
宏,以确保不会发生像@"lsatName"
这样的错误。
带有基本谓词的查询
NSPredicate *smithsPredicate = [NSPredicate predicateWithFormat:@"%K == %@", VOKKeyForInstanceOf(VOKPerson, lastName), @"Smith"];
// Passing `nil` for any managed object context parameter uses the main context
NSArray *allSmiths = [VOKPerson vok_fetchAllForPredicate:smithsPredicate forManagedObjectContext:nil];
使用基本谓词和排序查询
NSPredicate *smithsPredicate = [NSPredicate predicateWithFormat:@"%K == %@", VOKKeyForInstanceOf(VOKPerson, lastName), @"Smith"];
NSArray *sortedSmiths = [VOKPerson vok_fetchAllForPredicate:smithsPredicate
sortedByKey:VOKKeyForInstanceOf(VOKPerson, firstName)
ascending:YES
forManagedObjectContext:nil];
删除记录
NSPredicate *personPredicate = [NSPredicate predicateWithFormat:@"%K == %@", VOKKeyForInstanceOf(VOKPerson, ticketNumber), @"A14"];
VOKPerson *person = [VOKPerson vok_fetchForPredicate:personPredicate
forManagedObjectContext:nil];
[[VOKCoreDataManager sharedInstance] deleteObject:person];
[[VOKCoreDataManager sharedInstance] saveMainContextAndWait];
保存
//Saves the main context synchronously
[[VOKCoreDataManager sharedInstance] saveMainContextAndWait];
//Saves the main context asynchronously
[[VOKCoreDataManager sharedInstance] saveMainContext];
//Save a temp context and merge changes to the main context asynchronously
[[VOKCoreDataManager sharedInstance] saveAndMergeWithMainContext:tempContext];
//Save a temp context and merge changes to the main context synchronously
[[VOKCoreDataManager sharedInstance] saveAndMergeWithMainContextAndWait:tempContext];
注意:当调用这些方法中的任何一个时,都有一个私有上下文会异步地将更改保存到持久存储。主上下文是此“根”上下文的子上下文。如果您使用的是主上下文和临时上下文,则不应出现任何问题。如果您创建的一个上下文不是主上下文的子上下文,请注意这一点。
Swift 版本支持
对于 Swift 2:使用 Vokoder 版本 2.x 或 3.x。此支持正在在 swift2
分支维护。
对于 Swift 3:使用 Vokoder 版本 4.x 或 5.x。
对于 Swift 4:使用 Vokoder 版本 6.x。
对于 Swift 4.2:使用 Vokoder 版本 7.x。
对于 Swift 5:使用 Vokoder 版本 8.x。
许可
Vokoder 适用于 MIT 许可证。有关更多信息,请参阅 LICENSE 文件。