NSManagedObject подкласс вызывает NSInvalidArgumentException - PullRequest
0 голосов
/ 06 апреля 2011

Я пытаюсь создать подкласс для своих классов NSManagedObject, чтобы инкапсулировать мои процедуры get, set, save.Класс использует свой собственный managedObjectContext с общим persistentStoreCoordinator, так как это должно быть поточно-ориентированным.

Все вызовы метода без проблем, но когда я пытаюсь выполнить метод save:, я получаю следующую ошибку:

NSInvalidArgumentException', reason: '**-[MyEntity save:]: unrecognized selector sent to instance**'

Прилагается упрощенная версия, которая выдает ту же ошибку.

Вот код для подкласса:

@interface XXMyEntity : MyEntity {
@private
    NSManagedObjectContext * _managedObjectContext;
}

- (XXMyEntity *) init;
- (BOOL) save:(NSError **)error;

- (NSManagedObjectContext *) managedObjectContext;
- (NSPersistentStoreCoordinator *) persistentStoreCoordinator;

@end

@implementation XXMyEntity

- (XXMyEntity *) init 
{
    self = [NSEntityDescription insertNewObjectForEntityForName:@"MyEntity" inManagedObjectContext:[self managedObjectContext]];

    return self;
}

- (BOOL) save:(NSError **)error
{
    return [[self managedObjectContext] save:error];
}

- (NSManagedObjectContext *)managedObjectContext {

    if (_managedObjectContext != nil) {
        return _managedObjectContext;
    }

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil) {
        _managedObjectContext = [[NSManagedObjectContext alloc] init];
        [_managedObjectContext setPersistentStoreCoordinator:coordinator];
    }
    return _managedObjectContext;
}

- (NSPersistentStoreCoordinator *) persistentStoreCoordinator 
{
    newCoreDataAppDelegate * appDelegate = (newCoreDataAppDelegate *)[[UIApplication sharedApplication] delegate];
    return appDelegate.persistentStoreCoordinator;
}

- (void) dealloc
{
    [_managedObjectContext release];

    [super dealloc];
}
@end

Реализация:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Override point for customization after application launch.

    XXMyEntity * myEntity = [[XXMyEntity alloc]init];
    myEntity.id = [NSNumber numberWithInt:1];
    myEntity.title = @"My Title";

    [myEntity save:nil];

    [self.window makeKeyAndVisible];
    return YES;
}

Я также попытался изменить сигнатуру метода на что-то еще, например saveEntity, предполагая, что, возможно, ябезуспешно вмешивался в унаследованный метод.

Любая помощь приветствуется.

Ответы [ 3 ]

2 голосов
/ 06 апреля 2011

Я думаю, что ваша основная проблема заключается в том, что ваш init метод требует, чтобы у объекта уже был контекст управляемого объекта, даже если вы никогда не назначаете ему контекст.Конечно, вы не можете назначить его, потому что до init self не существует.Немного парадокса.Как указал Джо, вы все равно используете не ту сущность.

Вы не должны инициализировать подклассы управляемого объекта таким способом.Просто вставьте их в контекст, как если бы вы использовали общий управляемый объект, и контекст будет достаточно умен, чтобы вернуть правильный подкласс.Если вы хотите выполнить настройку, сделайте это методом awakeFromInsert.

1 голос
/ 06 апреля 2011

Плохое управление памятью с использованием функции init, так как вы как вызов для выделения предшествует init.Кроме того, хотя вы меняете значение self, оно не делает его типом XXMyEntity, оно все равно равно MyEntity, и именно поэтому вы получаете сообщение об ошибке.

Обновление

Чтобы заставить XXMyEntity работать, вам нужно открыть файл xcdatamodel и установить класс MyEntity в XXMyEntity.Также прочитайте Примечания по подклассам NSManagedObject.

0 голосов
/ 07 апреля 2011

Позвольте мне найти несколько неправильных вещей ...

  • Я предполагаю, что MyEntity является подклассом NSManagedObject.Какой смысл XXXMyEntity?

  • - (NSManagedObjectContext *) managedObjectContext;

    • NSManagedObject уже имеет метод -managedObjectContext.Переопределение может привести к путанице.
    • Вы возвращаете разные NSManagedObjectContext для каждого экземпляра XXXMyEntity.Управляемые объекты в разных контекстах не могут образовывать отношения между собой (ну, могут, но случаются плохие вещи).
  • self = [NSEntityDescription insertNewObjectForEntityForName:@"MyEntity" inManagedObjectContext:[self managedObjectContext]];

    • Вы теряете старое Я (A).
    • Вы не сохраняете новое Я (B).
    • Вы вызываете [self managedObjectContext] (A), но вставляетеНовый объект (B) в A управляемогоObjectContext.Однако, так как вы переопределили -managedObjectContext, [b managedObjectContext] вернет другой контекст.Это означает, что любые изменения в b могут быть забраны неправильно;трудно сказать.
    • Вызов +insertNewObjectForEntityForName:inManagedObjectContext: не возвращает экземпляр XXXMyEntity (как определено TechZen).
    • Даже если это так, +insertNewObjectForEntityForName:inManagedObjectContext: создает управляемый объект, используя+alloc и -init.Вы переопределили -init, так что это приведет к бесконечной рекурсии.
  • newCoreDataAppDelegate * appDelegate = (newCoreDataAppDelegate *)[[UIApplication sharedApplication] delegate];

    • UIKit в основном не является потоком-безопасен и должен быть доступен только из основного потока.Вызов -[UIApplication delegate] из потока, который не является основным, потенциально глуп.Если поток является основным потоком, то, возможно, вы все равно захотите использовать тот же контекст NSManagedObject.

Если вы пытаетесь использовать CoreДанные, которые волшебным образом сохраняются между запусками приложений, и вы выполняете большую часть обработки в главном потоке, у меня есть несколько предложений:

  • Выделите один NSManagedObjectContext для всего приложения.Возможно, вы захотите сохранить это в делегате приложения или в «синглтоне» или в чем-то подобном.
  • Используйте «удобные конструкторы» вместо переопределения -init:

    + (MyEntity) entity {NSManagedObjectContext * context = ...;MyEntity * entity = [NSEntityDescription insert ...];возвращаемая сущность;}

С оговорками:

  • Вам все еще нужен способ извлечения сущностей (вы могли бы сделать это с помощью более удобных конструкторов)
  • Youпо-прежнему необходимо удалять объекты, чтобы избежать увеличения базы данных.
  • Сохранение происходит довольно медленно.
  • Если вы решили сохранить данные в фоновом потоке, чтобы не блокировать пользовательский интерфейс, вынеобходимо тщательно просмотреть каждый фрагмент кода , который касается MyEntity, поскольку NSManagedObject и NSManagedObjectContext не являются поточно-ориентированными.

Также обратите внимание, что в некоторых случаях наличие нескольких MOC в одном и том жеТема имеет смысл: если у вас есть «редактировать» представление с кнопкой отмены, вы можете иметь отдельный MOC для экрана редактирования и не сохранять, если пользователь отменяет.В качестве альтернативы я думаю, что вы можете использовать один MOC и NSUndoManager.

Руководство по программированию базовых данных: обзор технологии дает достойный список того, что представляют из себя основные данные, а какие нет;Я подозреваю, что вы не используете его по назначению.

...