美文网首页
CoreData和MJExtension结合遇到的问题

CoreData和MJExtension结合遇到的问题

作者: 小米咸鱼 | 来源:发表于2019-03-07 13:38 被阅读0次

    最近在学习CoreData,在之前的学习Demo中进行测试学习,由于项目已经存在,现在需要引入CoreData框架。需做如下修改:
    1.需将已存在的model继承自NSManagedObject;
    2.创建.xcdatamodeld映射文件;
    3.添加需要使用CoreData的model实体(Entity);将Entity->Show the Data Model inspector->Codegen修改为Manual/None(即不自动生成实体类,因为model实体类已经存在了,如果修改的话会再次生成同名实体类,导致类文件重复错误)。


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

    实体类中NSArray、NSDictionary、NSData类型对应CoreData的Transformable类型。在与MJExtension结合使用时不能对属性为NSArray(保存自定义model)的字段采用映射文件关联关系方式。如果使用关联关系NSArray类型数

    采用关联关系One To Many 时属性类型必须是NSSet而非NSArray;Transformable则可以使用NSArray;但当与MJExtension结合使用时,Transformable类型属性不能使用NSSet,会导致数据丢失。
    总结(集合数据情况):
    1.采用关联关系:使用NSSet类型定义属性,NSArray会崩溃;
    2.Transformable类型:
    a)普通情况:使用NSArray/NSSet定义属性;
    b)与MJExtension结合使用:NSArray定义属性,NSSet会数据丢失。

    CoreData本地虽然对数据进行了缓存,但查询返回数据为空。此时应采用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对象转换成NSData进行存储,此时需要我们执行转换器,如果不指定在读取包含NSArray、NSDictionary类型属性(属性中保存自定义model)的model会报错

    -[XXX initWithCoder:]: unrecognized selector sent to instance 0x60000091a180
    
    

    系统为我们提供了NSValueTransformer转换器,在关系映射文件选中Transformable类型属性,在Show the Data Model inspector中的Value Transformer写入NSValueTransformer来指定转换器可以正常进行数据存取操作。


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

    但是NSValueTransformer是转换成NSData进行存取的,读取出的OC对象为NSData类型,需要再存进行转换,NSData->NSArray/NSDictionary。

    每次读取然后转换比较麻烦,方便起见,我们可以自定义转换器,在转换器中统一处理。继承自NSValueTransformer类,重写一下方法:

    - (nullable id)transformedValue:(nullable id)value;           // by default returns value
    - (nullable id)reverseTransformedValue:(nullable id)value; 
    

    例如:

    #import "ZTCoreDataTransformer.h"
    
    @implementation ZTCoreDataTransformer
    
    /**
     转化方法实现(如:将OC对象转换成Sqlite可存储的对象-序列化过程)
    
     @param value 待转换数据
     @return 转换结果
     */
    - (id)transformedValue:(id)value{
        if (value == nil) {
            return nil;
        }
        if ([value isKindOfClass:[NSArray class]] || [value isKindOfClass:[NSDictionary class]]){
            return [NSKeyedArchiver archivedDataWithRootObject:value];
        }
        return nil;
    }
    
    /**
     逆向转换实现(如:将Sqlite存储的对象转换成OC对象-反序列化过程)
    
     @param value 待转换数据
     @return 转换结果
     */
    - (id)reverseTransformedValue:(id)value{
        if (value) {
           return [NSKeyedUnarchiver unarchiveObjectWithData:value];
        }
        return nil;
    }
    @end
    

    然后将转换器指定为自定义的。

    采用归档(NSKeyedArchiver)方式进行本地化存储的类还需要实现NSCoding协议。
    1.一般model直接实现NSCoding协议即可;
    使用MJExtension直接在model的实现中使用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实现(.m)文件中使用ZTCoreDataCodingImplementation宏即可。shareManage是CoreData的上下文。我自定义了一个BaseMode。

    #import <Foundation/Foundation.h>
    #import <CoreData/CoreData.h>
    #import <MJExtension/MJExtension.h>
    
    /**
     需要CoreData本地化的模型集成此类;需创建与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
    

    个人笔记,还没整理

    相关文章

      网友评论

          本文标题:CoreData和MJExtension结合遇到的问题

          本文链接:https://www.haomeiwen.com/subject/fqynyqtx.html