CoreData和MJExtension結合遇到的問題

最近在學習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)存在了,如果修改的話會再次生成同名實體類,導致類文件重復錯誤)。


65C27B5A-512E-42F8-A73D-BF9FB1872EE1.png

實體類中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
3E2FE308-4A79-4D42-AE5B-CC9939191407.png

在使用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ù)存取操作。


D981DE14-5D2F-440E-812D-4F6F58851AFC.png

但是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

個人筆記,還沒整理

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容