Can NSManagedObject match NSCoding

I need to transfer one object to the device. Now I am converting my NSManagedObject into a dictionary, archiving it and sending it as NSData. Having received, I open it. But I would really like to migrate NSManagedObject itself, archiving and disarchiving instead of creating an intermediate data object.

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

The problem with the code above. It crashes with self.title in initWithCoder talking about an unrecognized selector sent to the instance.

  • Why title not recognized as a selector.
  • Should unarchive use the null context of managed objects somehow before creating the object in initWithCoder ?
  • Do I need to override copyWithZone ?
+6
source share
3 answers

This snippet below should do the trick. The main difference is calling 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; } 

Only attributes will be processed over the fragment, there are no links. Dealing with NSManagedObjectID relationships using NSCoding terrible. If you need to relate the relationship between considering introducing an additional attribute to match two (or many) objects when decoding.

how to get <YourContext>

(based on the now inaccessible Sam Soffes entry, code taken from https://gist.github.com/soffes/317794#file-ssmanagedobject-m )

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

Note: replace <YourContext> in the first fragment with mainContext

+9
source

The problem, obviously, is the unarchiver. After all, you cannot use both initWithEntity: and initWithCoder: in the same object. However, I suspect that with some tricks you can do the job. For example, we implement initWithCoder: as you did, and create another managed object with initWithEntity: (this means that you will need unmanaged ivars that can store such a link. Implement forwardingTargetForSelector: and if the object is the one created using initWithCoder: move it to the shadow object created with initWithEntity: (otherwise move this selector to super.) When the object is fully decoded, ask it about the real managed object, and you're done.

NOTE. I did not do this, but was very successful with forwardingTargetForSelector:

0
source

Obviously, NSManagedObject does not comply with NSCoding . You could try to create an appropriate subclass of a subclass of the managed entity, but at best this would be an insidious proposition. NSManagedObject must have an associated NSManagedObjectID . And you cannot assign an identifier for an object - this happens automatically when you create an object. Even if your subclass was NSCoding , you would need to find a way to unlock the object, and also allow the local context of the managed object to assign an object identifier.

And even this ignores the question of how you will handle relationships in managed objects.

Converting to / from NSDictionary really much better. But you cannot just unlock the data and complete it. On the receiving side, you need to create a new instance of the managed entity and set its attribute values ​​from the dictionary. You may be able to get your approach to work, but by the time you are done there will be more work and more code than if you were using NSDictionary .

Seriously: NSCoding , initWithCoder: copyWithZone: etc. - This is a really bad idea for the problem you are trying to solve. NSCoding is good for many situations, but it is not suitable here.

0
source

Source: https://habr.com/ru/post/944525/


All Articles