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器。 - 在创build
initWithCoder
的对象之前,是否应该使用一个零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的代码)
+ (NSManagedObjectContext *)mainContext { AppDelegate *appDelegate = [AppDelegate sharedAppDelegate]; return [appDelegate managedObjectContext]; }
注意:用mainContext
replace第一个代码片段中的<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
,将会有更多的工作和更多的代码。
认真: NSCoding
, initWithCoder:
, copyWithZone:
等,是一个非常糟糕的想法,你想解决的问题。 NSCoding
很适合很多情况,但在这里并不合适。