将NSCoding与Realm模型对象一起使用

时间:2015-04-10 05:50:35

标签: objective-c realm nscoding

我已经在我的显示器上敲了好几个星期,在休息一下后绕过我的问题,我发现我再次遇到同样的问题。

首先,我能找到与我的问题最接近的事情: NSCoding / Realm in Google Groups

我试图在我的一些Realm Model对象上使用NSCoding协议,以便将它们变成标准的NSData对象,我可以在我的项目正在使用的几个框架中进行转换。但是,我收到了一些奇怪的错误,我根本无法找到解决方法,而且我只是没有足够的经验来处理。

我使用的是简单的NSKeyedArchiver,特别是archivedDataWithRootObject。编码过程看起来非常顺利,直到我可以将其二进制blob作为NSData传输而没有问题 - 字节为字节,编码的大小是解码器端接收到的。我的模型定义如下:

//
//  CSTaskRealmModel.m
//  CommSync
//
//  Created by Ivan Lugo on 1/27/15.
//  Copyright (c) 2015 AppsByDLI. All rights reserved.
//

#import "CSTaskRealmModel.h"

@implementation CSTaskRealmModel

#pragma mark - Realm modeling protocol

+ (NSDictionary *)defaultPropertyValues {

    NSDictionary* defaults = nil;

    defaults = @{@"taskDescription":@"",
                 @"taskTitle":@"",
                 @"taskPriority":[NSNumber numberWithInt:0],

                 @"UUID":@"",
                 @"deviceID":@"",
                 @"concatenatedID":@"",
                 @"assignedID":@"",
                 @"tag":@"",
                 @"completed":@false

                 };

    return defaults;
}

+ (NSArray*)ignoredProperties {
    return @[@"TRANSIENT_audioDataURL"];
}

+ (NSString*)primaryKey {
    return @"concatenatedID";
}

#pragma mark - NSCoding Compliance

- (id) initWithCoder:(NSCoder *)aDecoder {

if(self = [super init]) {
        self.UUID = [aDecoder decodeObjectForKey:kUUID];
        self.deviceID = [aDecoder decodeObjectForKey:kDeviceId];
        self.concatenatedID = [aDecoder decodeObjectForKey:kConcatenatedID];
        self.assignedID = [aDecoder decodeObjectForKey:kAssignedID];
        self.tag = [aDecoder decodeObjectForKey:kTag];

        self.taskTitle = [aDecoder decodeObjectForKey:kTaskTitle];
        self.taskDescription = [aDecoder decodeObjectForKey:kTaskDescription];

        NSNumber* num = [aDecoder decodeObjectForKey:kCompleted];
        self.completed = [num boolValue];
        num = [aDecoder decodeObjectForKey:kTaskPriority];
        self.taskPriority = [num integerValue];

        NSMutableArray* dataArray = [aDecoder decodeObjectForKey:kRevisionDataArray];
        for(CSTaskRevisionRealmModel* rev in dataArray) {
            [self.revisions addObject:rev];
        }

        dataArray = [aDecoder decodeObjectForKey:kMediaDataArray];
        for(CSTaskMediaRealmModel* media in dataArray) {
            [self.taskMedia addObject:media];
        }

        dataArray = [aDecoder decodeObjectForKey:kCommentsDataArray];
        for (CSCommentRealmModel* comment in dataArray) {
            [self.comments addObject:comment];
        }
    }

    return self;
}

- (void) encodeWithCoder:(NSCoder *)aCoder {

    [aCoder encodeObject:_UUID forKey:kUUID];
    [aCoder encodeObject:_deviceID forKey:kDeviceId];
    [aCoder encodeObject:_concatenatedID forKey:kConcatenatedID];
    [aCoder encodeObject:_assignedID forKey:kAssignedID];
    [aCoder encodeObject:_tag forKey:kTag];
    [aCoder encodeObject: [NSNumber numberWithBool:_completed] forKey:kCompleted];
    [aCoder encodeObject: _taskTitle forKey:kTaskTitle];
    [aCoder encodeObject:_taskDescription forKey:kTaskDescription];
    [aCoder encodeObject:[NSNumber numberWithInteger:_taskPriority] forKey:kTaskPriority];

    NSMutableArray* revArray = [NSMutableArray arrayWithCapacity:_revisions.count];
    for (CSTaskRevisionRealmModel* rev in self.revisions) {
        [revArray addObject:rev];
    }
    [aCoder encodeObject:revArray forKey:kRevisionDataArray];

    NSMutableArray* mediaArray = [NSMutableArray arrayWithCapacity:_taskMedia.count];
    for (CSTaskMediaRealmModel* media in self.taskMedia) {
        [mediaArray addObject:media];
    }
    [aCoder encodeObject:mediaArray forKey:kMediaDataArray];

    NSMutableArray* commentsArray = [NSMutableArray arrayWithCapacity:_comments.count];
    for (CSCommentRealmModel* comment in self.comments) {
        [commentsArray addObject:comment];
    }
    [aCoder encodeObject:commentsArray forKey:kCommentsDataArray];
}

@end

然而,当对象解码时,在这些非常无害的线路上......

NSData* taskData = [NSData dataWithContentsOfURL:localURL];
id newTask = [NSKeyedUnarchiver unarchiveObjectWithData:taskData];
// Where 'localURL' is just an on-disk location of the data object a device has received. 

我收到此错误;它出现在Realm源代码的RLMAccessor。:

*** Terminating app due to uncaught exception 'RLMException', reason: 'Object has been deleted or invalidated.'

在我看来,好像在模型的init方法中发生了非常非常奇怪的事情,我无法做出正面或反面。在某些情况下,如果我删除主键(尝试绕过失效),错误将更改为(在同一源文件中):

'Primary key can't be changed after an object is inserted.'

我根本无法弄清楚这里发生了什么!这是踢球者:我的原始实现,在我设法为其添加一些属性之前,完美地工作

//
//  CSTaskRealmModel.m
//  CommSync
//
//  Created by Ivan Lugo on 1/27/15.
//  Copyright (c) 2015 AppsByDLI. All rights reserved.
//

#import "CSTaskRealmModel.h"

@implementation CSTaskRealmModel

#pragma mark - Lifecycle
- (id)initWithCoder:(NSCoder *)aDecoder
{
    if (self = [super init]) {
        self.UUID = [aDecoder decodeObjectForKey:@"UUID"];
        self.deviceID = [aDecoder decodeObjectForKey:@"deviceID"];
        self.concatenatedID = [aDecoder decodeObjectForKey:@"concatenatedID"];

        self.taskTitle = [aDecoder decodeObjectForKey:@"taskTitle"];
        self.taskDescription = [aDecoder decodeObjectForKey:@"taskDescripion"];
        self.taskPriority = [aDecoder decodeIntForKey:@"taskPriority"];

    }
    return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder
{
    [aCoder encodeObject:self.UUID forKey:@"UUID"];
    [aCoder encodeObject:self.deviceID forKey:@"deviceID"];
    [aCoder encodeObject:self.concatenatedID forKey:@"concatenatedID"];

    [aCoder encodeObject:self.taskTitle forKey:@"taskTitle"];
    [aCoder encodeObject:self.taskDescription forKey:@"taskDescripion"];
    [aCoder encodeInteger:self.taskPriority forKey:@"taskPriority"];

    // NOTE!
    // This is ALL KINDS OF EXTREMELY INEFFICIENT!
    // We should not rearchive and reconvert images we have already worked with
    NSMutableArray* tempArrayOfImages = [NSMutableArray arrayWithCapacity:self.TRANSIENT_taskImages.count];
    for(UIImage* image in self.TRANSIENT_taskImages) { // for every TRANSIENT UIImage we have on this task
        NSData* thisImage = UIImageJPEGRepresentation(image, 0.3); // make a new JPEG data object with some compressed size
        [tempArrayOfImages addObject:thisImage]; // add it to our container
    }

    NSData* archivedImages = [NSKeyedArchiver archivedDataWithRootObject:tempArrayOfImages]; // archive the data ...

    [[RLMRealm defaultRealm] beginWriteTransaction];
    self.taskImages_NSDataArray_JPEG = archivedImages; // and set the images of this task to the new archive
    [[RLMRealm defaultRealm] commitWriteTransaction];

    [aCoder encodeObject:self.taskImages_NSDataArray_JPEG forKey:@"taskImages"]; // encode the object and pray
}

+ (NSArray*)ignoredProperties {

    return @[@"TRANSIENT_taskImages"];
}

请注意,我能看到的唯一重要区别是原始实现上没有primaryKey对象。我能想到的唯一能做错的事情是:(a)我从根本上对我的NSCoding线做错了,或者我甚至不应该对这些域对象进行NS编码,或者(b)某种其他方式,其他一些我的代码区域正在执行某些来验证Realm对象,然后将它打包并以NSData的形式发送出来。

我无法正确表达(现在是凌晨2点,发布时间......)我对您对此有任何见解或帮助表示感谢。如果您想要更多的代码上下文,我很乐意发布它,或者指向我的git存储库的方向。

1 个答案:

答案 0 :(得分:2)

看起来有点休息,需要更加巧妙的调试来破解这个。我欢迎对此答案进行任何更正,或者更深入的信息!

事实证明,访问Realm对象并不 实际上获取领域模型对象本身 - 或者至少不是NSCoding协议所期望的方式。在调试过程中,我发现未归档的类最终为RLMAccessor_[myClassName]。在预感中,我决定尝试别的东西。

我不是仅仅抓住我对编码感兴趣的领域对象,而是实例化一个全新的模型,并从感兴趣的模型中为其分配所有关键属性。然后,没有将它添加到领域,通过该行发送编码版本。事实证明,这非常有效。

出于某种原因,我还没有完全理解,那个返回的领域访问器使它从中提取数据的对象无效,这意味着它不能被重用。

我的方法可能不是最有效的,也可能不是Realm的做法,但它似乎符合我的需要:一个可以充当瞬态和持久对象的Realm模型对象。