NSManagedObject是否符合NSCoding

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

@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]; } 

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

  • 为什么title不被认为是一个select器。
  • 在创buildinitWithCoder的对象之前,是否应该使用一个零pipe理对象上下文?
  • 我是否需要重写copyWithZone

下面的代码片段应该能够做到这一点。 主要区别是调用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现在不可用的post,取自https://gist.github.com/soffes/317794#file-ssmanagedobject-m的代码&#xFF09;

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

注意:用mainContextreplace第一个代码片段中的<YourContext>

问题显然是unarchiver。 最后,在同一个对象中,没有办法同时使用initWithEntity:initWithCoder: 不过,我怀疑,有一些欺骗,你可能能够做到这一点。 例如,像你所做的那样实现initWithCoder:initWithCoder:中创build另一个托pipe对象initWithEntity:这意味着你将需要非托pipe的ivars来存放这样的引用);实现forwardingTargetForSelector: :,如果这个对象是被创build的对象initWithCoder:将它转发到你用initWithEntity:创build的阴影对象initWithEntity:否则,将该select器转发到超级)。当对象完全解码时,请求它为真正的托pipe对象,然后就完成了。

注意:我没有这样做,但是使用forwardingTargetForSelector:获得了很大的成功。

NSManagedObject显然不符合NSCoding 。 你可以尝试使一个自定义的托pipe对象的子类保持一致,但是最好的情况是它会是一个冒险的命题。 一个NSManagedObject必须有一个相关的NSManagedObjectID 。 而且,您不必分配对象ID – 创build对象时会自动发生。 即使你让你的子类符合NSCoding ,你也必须find一种解除对象的方法,同时也允许本地pipe理的对象上下文分配一个对象ID。

即使这样也忽略了如何处理pipe理对象上的关系。

转换成NSDictionary /从一个NSDictionary是一个更好的方法。 但是你不能只是将数据解压缩并完成。 在接收端,您需要创build一个新的托pipe对象实例,并从字典中设置其属性值。 有可能让你的方法工作,但是到你完成的时候,如果你只是使用NSDictionary ,将会有更多的工作和更多的代码。

认真: NSCodinginitWithCoder:copyWithZone:等,是一个非常糟糕的想法,你想解决的问题。 NSCoding很适合很多情况,但在这里并不合适。