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

标签 objective-c realm nscoding

几周来,我一直在用我的显示器敲打我的头,在绕过我的问题休息一下后,我发现我又遇到了同样的问题。

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

我正尝试在我的一些 Realm 模型对象上使用 NSCoding 协议(protocol),以便将它们变成标准的 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 行有问题,或者我什至不应该对这些 Realm 对象进行 NSCoding,或者(b)不知何故,其他一些在将 Realm 对象打包并作为 NSData 发送出去之前,我的代码区域正在对 Realm 对象进行某些验证。

我无法准确表达(现在是凌晨 2:00,发帖时间...)我感谢您对此提供的任何见解或帮助。如果您想要更多代码上下文,我很乐意将其发布,或者为您指明我的 git 存储库的方向。

最佳答案

看起来有点中断,需要更巧妙的调试才能破解此问题。我欢迎对此答案的任何更正,或任何更深入的信息!

事实证明,访问 Realm 对象实际上不会让您获得 Realm 模型对象本身 - 或者至少,不是 NSCoding 协议(protocol)期望的方式.在调试期间,我发现未归档的类最终成为 RLMAccessor_[myClassName]。凭直觉,我决定尝试别的东西。

我不是仅仅获取我对编码感兴趣的 Realm 对象,而是实例化了一个全新的模型,并将感兴趣的模型中的所有关键属性分配给它。然后,将它添加到 Realm ,通过线路发送它的编码版本。事实证明,这非常有效。

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

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

关于objective-c - 将 NSCoding 与 Realm 模型对象一起使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29554276/

相关文章:

ios - 隐藏类型为 UIKeyboardTypeNumberPad objective c 的键盘

iphone - 如何删除主从模板上的编辑按钮

ios - 与 Swift Realm 的继承,混淆

ios - 无法将 Swift 和 Obj-C 库与 Cocoapods 一起使用

ios - 类不符合 NSCoding 协议(protocol)

ios - 以编程方式添加约束

iphone - 测试 iPhone 的 IP 地址可达性

java - Realm ORM : how to deal with Maps?

ios - Swift 编译器警告 : Result of call to 'save(defaults:)' is unused

swift - 不兼容对象的 NSCoding (MKMapItem)