6

我需要跨设备传输单个对象。现在我正在将我的 NSManagedObject 转换为字典,将其存档并作为 NSData 发送。收到后,我正在取消存档。但我真的很想通过归档和取消归档而不是创建中间数据对象来传输 NSManagedObject 本身。

@interface Test : NSManagedObject<NSCoding>
@property (nonatomic, retain) NSString * title;
@end

@implementation Test
@dynamic title;

- (id)initWithCoder:(NSCoder *)coder {
    self = [super init];
    if (self) {
        self.title = [coder decodeObjectForKey:@"title"]; //<CRASH
    }
    return self;
}
- (void)encodeWithCoder:(NSCoder *)coder {
    [coder encodeObject:self.title forKey:@"title"];
}
@end


NSData *archivedObjects = [NSKeyedArchiver archivedDataWithRootObject:testObj];
NSData *objectsData = archivedObjects;
if ([objectsData length] > 0) {
    NSArray *objects = [NSKeyedUnarchiver unarchiveObjectWithData:objectsData];
}

上面代码的问题是。self.title它在initWithCoder说无法识别的选择器发送到实例时崩溃。

  • 为什么title不被识别为选择器。
  • 在创建对象之前,是否应该取消归档以某种方式使用 nil 托管对象上下文initWithCoder
  • 我需要覆盖copyWithZone吗?
4

3 回答 3

9

下面的这个片段应该可以解决问题。主要区别是调用super initWithEntity:insertIntoManagedObjectContext:

- (id)initWithCoder:(NSCoder *)aDecoder {
   NSEntityDescription *entity = [NSEntityDescription entityForName:@"Test" inManagedObjectContext:<YourContext>];

   self = [super initWithEntity:entity insertIntoManagedObjectContext:nil];
   NSArray * attributeNameArray = [[NSArray alloc] initWithArray:self.entity.attributesByName.allKeys];

   for (NSString * attributeName in attributeNameArray) {
        [self setValue:[aDecoder decodeObjectForKey:attributeName] forKey:attributeName];
   }
   return self;
}

上面的代码片段将只处理属性,没有关系。NSManagedObjectID用使用来处理关系NSCoding是可怕的。如果您确实需要跨越关系,请考虑在解码时引入一个额外的属性来匹配两个(或多个)实体。

如何获得<YourContext>

(基于 Sam Soffes 现在不可用的帖子,代码取自https://gist.github.com/soffes/317794#file-ssmanagedobject-m

+ (NSManagedObjectContext *)mainContext {
     AppDelegate *appDelegate = [AppDelegate sharedAppDelegate];
return [appDelegate managedObjectContext];
}

注意:<YourContext>在第一个片段中替换为mainContext

于 2013-05-09T08:20:56.210 回答
1

显然NSManagedObject不符合NSCoding。您可以尝试使自定义托管对象子类符合要求,但这充其量只是一个冒险的提议。一个NSManagedObject必须有一个相关的NSManagedObjectID。而且,您无需分配对象 ID——创建对象时会自动发生。即使您使您的子类符合NSCoding,您也必须找到一种方法来取消归档对象,同时还允许本地托管对象上下文分配对象 ID。

甚至这忽略了如何处理托管对象上的关系的问题。

转换为/从一个NSDictionary确实是一种更好的方法。但是您不能只是取消归档数据并完成。在接收端,您需要创建一个新的托管对象实例并从字典中设置其属性值。也许可以让你的方法发挥作用,但是当你完成时,它会比你只使用NSDictionary.

说真的:NSCoding, initWithCoder:,copyWithZone:等对于您要解决的问题来说是一个非常糟糕的主意。NSCoding在许多情况下都很好,但在这里不合适。

于 2013-05-08T16:42:16.930 回答
0

问题显然是取消归档。最后,没有办法在同一个对象中同时initWithEntity:使用两者。initWithCoder:但是,我怀疑通过一些技巧,您也许可以完成这项工作。例如,initWithCoder:按照您所做的那样实现,并在其中创建另一个托管对象initWithEntity:(这意味着您将需要可以保存此类引用的非托管 ivars。实现forwardingTargetForSelector:,如果该对象是使用创建的对象,则将其initWithCoder:转发到影子你创建的对象initWithEntity:(否则,将该选择器转发给超级对象。当对象被完全解码后,然后向它询问真正的托管对象,你就完成了。

注意:我没有这样做,但在forwardingTargetForSelector:.

于 2013-05-08T13:42:27.623 回答