Может ли NSManagedObject соответствовать NSCoding

Мне нужно передать один объект через устройство. Прямо сейчас я конвертирую свой 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 не распознается как селектор.
  • Следует ли при разархивировании каким-либо образом использовать нулевой контекст управляемого объекта перед созданием объекта в initWithCoder?
  • Мне нужно переопределить copyWithZone?

person darshansonde    schedule 08.05.2013    source источник


Ответы (3)


Этот фрагмент ниже должен помочь. Основное отличие заключается в вызове 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>

(на основе недоступного в настоящее время сообщения Сэма Соффеса, код взят из https://gist.github.com/soffes/317794#file-ssmanagedobject-m)

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

Примечание: замените <YourContext> в первом фрагменте на mainContext

person Olaf    schedule 09.05.2013
comment
Ссылка о том, как получить ‹YourContext›, не работает. Вы можете его обновить? - person Chuck Krutsinger; 18.07.2017
comment
Исходный сайт оффлайн, но я нашел старую суть и скопировал оттуда код. надеюсь, это поможет - person Olaf; 24.07.2017
comment
Управляемый контекст в этой строке намеренно равен нулю? self = [super initWithEntity:entity insertIntoManagedObjectContext:nil]; - person Chuck Krutsinger; 12.12.2017

Проблема явно в разархивере. В конце концов, невозможно использовать одновременно initWithEntity: и initWithCoder: в одном объекте. Однако я подозреваю, что, возможно, вам удастся добиться этого с помощью некоторых уловок. Например, реализуйте initWithCoder:, как вы это сделали, и при этом создайте другой управляемый объект с initWithEntity: (это означает, что вам понадобятся неуправляемые ivars, которые могут содержать такую ​​ссылку. Реализуйте forwardingTargetForSelector:, и если объект создается с помощью initWithCoder:, вперед его к теневому объекту, который вы создали с помощью initWithEntity: (в противном случае, переадресуйте этот селектор на super). Когда объект полностью декодирован, спросите его о реальном управляемом объекте, и все готово.

ПРИМЕЧАНИЕ: Я не делал этого, но добился большого успеха с forwardingTargetForSelector:.

person David H    schedule 08.05.2013

Очевидно NSManagedObject не соответствует NSCoding. Вы можете попытаться согласовать собственный подкласс управляемого объекта, но это будет в лучшем случае рискованное предложение. NSManagedObject должен иметь связанный NSManagedObjectID. И вы не можете назначить идентификатор объекта - это происходит автоматически при создании объекта. Даже если вы сделали свой подкласс совместимым с NSCoding, вам нужно будет найти способ разархивировать объект, а также разрешить локальному контексту управляемого объекта назначать идентификатор объекта.

И даже при этом игнорируется вопрос о том, как вы будете обрабатывать отношения между управляемыми объектами.

Преобразование в / из NSDictionary - действительно лучший подход. Но вы не можете просто разархивировать данные и закончить. На принимающей стороне вам необходимо создать новый экземпляр управляемого объекта и установить значения его атрибутов из словаря. Возможно удастся заставить ваш подход работать, но к тому времени, когда вы закончите, это будет больше работы и больше кода, чем если бы вы просто использовали NSDictionary.

Серьезно: NSCoding, initWithCoder:, copyWithZone: и т. Д. - действительно плохая идея для решения проблемы, которую вы пытаетесь решить. NSCoding подходит для многих ситуаций, но здесь не подходит.

person Tom Harrington    schedule 08.05.2013
comment
согласился, что NSCoding для NSManagedObject - плохая идея. должен быть возможен общий способ передачи / сериализации NSManagedObject. - person darshansonde; 10.05.2013