最近在學習CoreData,在之前的學習Demo中進行測試學習,由于項目已經(jīng)存在,現(xiàn)在需要引入CoreData框架。需做如下修改:
1.需將已存在的model繼承自NSManagedObject;
2.創(chuàng)建.xcdatamodeld映射文件;
3.添加需要使用CoreData的model實體(Entity);將Entity->Show the Data Model inspector->Codegen修改為Manual/None(即不自動生成實體類,因為model實體類已經(jīng)存在了,如果修改的話會再次生成同名實體類,導致類文件重復錯誤)。

實體類中NSArray、NSDictionary、NSData類型對應CoreData的Transformable類型。在與MJExtension結合使用時不能對屬性為NSArray(保存自定義model)的字段采用映射文件關聯(lián)關系方式。如果使用關聯(lián)關系NSArray類型數(shù)
采用關聯(lián)關系One To Many 時屬性類型必須是NSSet而非NSArray;Transformable則可以使用NSArray;但當與MJExtension結合使用時,Transformable類型屬性不能使用NSSet,會導致數(shù)據(jù)丟失。
總結(集合數(shù)據(jù)情況):
1.采用關聯(lián)關系:使用NSSet類型定義屬性,NSArray會崩潰;
2.Transformable類型:
a)普通情況:使用NSArray/NSSet定義屬性;
b)與MJExtension結合使用:NSArray定義屬性,NSSet會數(shù)據(jù)丟失。
CoreData本地雖然對數(shù)據(jù)進行了緩存,但查詢返回數(shù)據(jù)為空。此時應采用Transformable類型。
#import "ZTCDBaseModel.h"
#import "UserInfo.h"
@interface User : ZTCDBaseModel
@property(nonatomic,copy) NSString* userName;
@property(nonatomic,copy) NSString* password;
@property(nonatomic,copy) NSArray<User*>* childs;
@property(nonatomic,strong) UserInfo* info;
@end

在使用Transformable類型時,實際上是將NSArray、NSDictionary對象轉(zhuǎn)換成NSData進行存儲,此時需要我們執(zhí)行轉(zhuǎn)換器,如果不指定在讀取包含NSArray、NSDictionary類型屬性(屬性中保存自定義model)的model會報錯
-[XXX initWithCoder:]: unrecognized selector sent to instance 0x60000091a180
系統(tǒng)為我們提供了NSValueTransformer轉(zhuǎn)換器,在關系映射文件選中Transformable類型屬性,在Show the Data Model inspector中的Value Transformer寫入NSValueTransformer來指定轉(zhuǎn)換器可以正常進行數(shù)據(jù)存取操作。

但是NSValueTransformer是轉(zhuǎn)換成NSData進行存取的,讀取出的OC對象為NSData類型,需要再存進行轉(zhuǎn)換,NSData->NSArray/NSDictionary。
每次讀取然后轉(zhuǎn)換比較麻煩,方便起見,我們可以自定義轉(zhuǎn)換器,在轉(zhuǎn)換器中統(tǒng)一處理。繼承自NSValueTransformer類,重寫一下方法:
- (nullable id)transformedValue:(nullable id)value; // by default returns value
- (nullable id)reverseTransformedValue:(nullable id)value;
例如:
#import "ZTCoreDataTransformer.h"
@implementation ZTCoreDataTransformer
/**
轉(zhuǎn)化方法實現(xiàn)(如:將OC對象轉(zhuǎn)換成Sqlite可存儲的對象-序列化過程)
@param value 待轉(zhuǎn)換數(shù)據(jù)
@return 轉(zhuǎn)換結果
*/
- (id)transformedValue:(id)value{
if (value == nil) {
return nil;
}
if ([value isKindOfClass:[NSArray class]] || [value isKindOfClass:[NSDictionary class]]){
return [NSKeyedArchiver archivedDataWithRootObject:value];
}
return nil;
}
/**
逆向轉(zhuǎn)換實現(xiàn)(如:將Sqlite存儲的對象轉(zhuǎn)換成OC對象-反序列化過程)
@param value 待轉(zhuǎn)換數(shù)據(jù)
@return 轉(zhuǎn)換結果
*/
- (id)reverseTransformedValue:(id)value{
if (value) {
return [NSKeyedUnarchiver unarchiveObjectWithData:value];
}
return nil;
}
@end
然后將轉(zhuǎn)換器指定為自定義的。
采用歸檔(NSKeyedArchiver)方式進行本地化存儲的類還需要實現(xiàn)NSCoding協(xié)議。
1.一般model直接實現(xiàn)NSCoding協(xié)議即可;
使用MJExtension直接在model的實現(xiàn)中使用MJCodingImplementation宏即可;
2.當需要歸檔的model對象繼承自NSManagedObject時,無法通過init進行初始化。
- (void)encodeWithCoder:(NSCoder *)aCoder{
[aCoder encodeObject:self.userName forKey:@"userName"];
[aCoder encodeObject:self.password forKey:@"password"];
[aCoder encodeObject:self.childs forKey:@"childs"];
[aCoder encodeObject:self.info forKey:@"info"];
}
- (id)initWithCoder:(NSCoder *)aDecoder{
NSEntityDescription* descr = [NSEntityDescription entityForName:NSStringFromClass(self.class) inManagedObjectContext:self.class.shareManage];
self = [[self.class alloc] initWithEntity:descr insertIntoManagedObjectContext:nil];
if (self){
self.userName = [aDecoder decodeObjectForKey:@"userName"];
self.password = [aDecoder decodeObjectForKey:@"password"];
self.childs = [aDecoder decodeObjectForKey:@"childs"];
self.info = [aDecoder decodeObjectForKey:@"info"];
}
return self;
}
使用MJExtension時需自定義宏:
#define ZTCoreDataCodingImplementation \
- (id)initWithCoder:(NSCoder *)decoder \
{ \
NSEntityDescription* descr = [NSEntityDescription entityForName:NSStringFromClass(self.class) inManagedObjectContext:self.class.shareManage];\
self = [[self.class alloc] initWithEntity:descr insertIntoManagedObjectContext:nil];\
if (self) { \
[self mj_decode:decoder]; \
} \
return self; \
} \
\
- (void)encodeWithCoder:(NSCoder *)encoder \
{ \
[self mj_encode:encoder]; \
}
在model實現(xiàn)(.m)文件中使用ZTCoreDataCodingImplementation宏即可。shareManage是CoreData的上下文。我自定義了一個BaseMode。
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#import <MJExtension/MJExtension.h>
/**
需要CoreData本地化的模型集成此類;需創(chuàng)建與boundle id最后名稱一致的.xcdatamodeld映射文件
*/
@interface ZTCDBaseModel : NSManagedObject
+ (NSManagedObjectContext*)shareManage;
+ (id)ZT_JSONToModel:(id)JSON;
+ (id)ZT_fetchModel:(NSDictionary*)fetchParams;
@end
#import "ZTCDBaseModel.h"
@interface ZTCDBaseModel()
@end
@implementation ZTCDBaseModel
+ (NSManagedObjectContext*)shareManage{
static NSManagedObjectContext* context;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
NSString* modelName = [[[NSBundle mainBundle] bundleIdentifier] componentsSeparatedByString:@"."].lastObject;
NSURL* url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:modelName ofType:@"momd"]];
if (!url) {
NSString* errorMsg = [NSString stringWithFormat:@"%@.xcdatamodeld文件不存在!??!",modelName];
NSAssert(url,errorMsg);
}
NSManagedObjectModel* model = [[NSManagedObjectModel alloc] initWithContentsOfURL:url];
NSPersistentStoreCoordinator* storeCoordicate = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
NSString* dataPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject stringByAppendingFormat:@"/%@.sqlite",modelName];
[storeCoordicate addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[NSURL fileURLWithPath:dataPath] options:nil error:nil];
context.persistentStoreCoordinator = storeCoordicate;
context.undoManager = nil;
});
return context;
}
+ (id)ZT_JSONToModel:(id)JSON{
if (!JSON) {
return nil;
}
id result = [JSON isKindOfClass:[NSArray class]] ? [self.class mj_objectArrayWithKeyValuesArray:JSON context:self.shareManage] : [self.class mj_objectWithKeyValues:JSON context:self.shareManage];
NSError* error;
[self.shareManage save:&error];
return error ? nil : result;
}
@end
個人筆記,還沒整理