Нужна помощь в понимании ошибок в Core Data

У меня есть объект Core Data, который использует пользовательский подкласс NSManagedObject. У меня есть несколько переменных и свойств экземпляра, которые не связаны с атрибутами или отношениями Core Data. Пример того, что я делаю, таков:

@interface Root : NSManagedObject
{
    Foo *foo;
    Bar *bar;
}

@property (nonatomic, retain) Foo *foo;
@property (nonatomic, retain) Bar *bar;

// Core Data generated
@property (nonatomic, retain) Node *relationship;
@property (nonatomic, retain) NSString *attribute;

-(void)awake;

@end


@implementation Root

@synthesize foo;
@synthesize bar;

@dynamic relationship;
@dynamic attribute;

-(void)awakeFromInsert {
    [super awakeFromInsert];
    [self awake];
}

-(void)awakeFromFetch {
    [super awakeFromFetch];
    [self awake];
}

-(void)awake {
    Foo *newFoo = [[Foo alloc] init];
    self.foo = newFoo;
    [newFoo release];

    // bar is not automatically initialized, but can be set by something external
}

-(void)didTurnIntoFault {
    [foo release];
    foo = nil;
    [bar release];
    bar = nil;

    [super didTurnIntoFault];
}

@end

Теперь в моем приложении я получаю экземпляр Root по запросу на выборку один раз при запуске приложения и сохраняю его до тех пор, пока приложение не закроется. (На самом деле это немного сложнее, потому что вы можете удалить корневой экземпляр и создать новый, но максимум один существует одновременно и сохраняется.) Поэтому я надеюсь, что didTurnIntoFault никогда не будет вызываться, пока мое приложение не завершит работу. Если бы это было так, то в какой-то момент я бы обратился к root.foo или root.bar и получил бы nil. Это было бы ошибкой для моего приложения. Экземпляр Root всегда должен иметь ненулевое значение для foo и bar; foo создается всякий раз, когда загружается экземпляр, а bar устанавливается вызывающей стороной сразу после получения корневого экземпляра.

Могу ли я полагаться на то, что didTurnIntoFault не вызывается, если мой код сохраняет NSManagedObject?

Если я не хочу, чтобы вызывалась didTurnInfoFault, зачем она мне? Ну, мне нужно как-нибудь почиститься. Может быть, мне действительно следует поместить этот код в Dealloc, если я не хочу, чтобы эти переменные экземпляра были освобождены, пока программа не завершит работу. Но я думаю, что я читал некоторую документацию, в которой не рекомендуется использовать Dealloc для подклассов NSManagedObject.


person morningstar    schedule 21.09.2011    source источник
comment
Объект может быть превращен в ошибку в любое время, если он действительно существует во внутреннем хранилище и есть предупреждение о нехватке памяти.   -  person Mike Weller    schedule 21.09.2011


Ответы (2)


Если ваш объект существует в базовом хранилище файлов, он потенциально может выйти из строя в любое время из-за предупреждений о нехватке памяти и т. д.

Но из вашего кода кажется, что все, что вам нужно, это простое свойство с ленивой инициализацией, т.е. переопределение геттера «foo» следующим образом:

- (Foo *)foo
{
    if (!foo) {
        foo = [[Foo alloc] init];
    }

    return foo; // or [[foo retain] autorelease] if you need that safety
}

Это гарантированно не возвращает nil, и вы все равно можете освободить и обнулить переменную экземпляра, если хотите, в didTurnIntoFault. При следующем вызове геттера новый ivar будет инициализирован снова.

person Mike Weller    schedule 21.09.2011
comment
Что если я скажу вам, что Foo на самом деле NSOperationQueue? (Да, вероятно, плохая идея, но я сделал это, когда был новичком в Core Data и думал, что вы можете обращаться с объектами Core Data как с обычными объектами.) А как насчет бара? Тот же трюк не сработает, потому что он установлен извне. Когда Root снова выйдет из строя, ему придется каким-то образом подсказать внешнему объекту, чтобы он снова предоставил ему экземпляр Bar. - person morningstar; 21.09.2011
comment
Хорошо, спасибо за информацию. Я не думаю, что рекомендация сработает, но я знаю, с чем мне придется иметь дело. - person morningstar; 23.09.2011

Моя интерпретация документации заключается в том, что ваш объект не будет снова превращен в ошибку, если вы не сделаете это самостоятельно (refreshObject:mergeChanges и несколько других методов в документации: http://developer.apple.com/библиотека/mac/documentation/Cocoa/Conceptual/CoreData/Articles/cdPerformance.html#//apple_ref/doc/uid/TP40003468-SW4). Он также явно говорит вам не переопределять Dealloc, поэтому я бы сказал, что вы поступаете правильно. Хотя было бы очень интересно услышать и другие мысли на эту тему.

person jrturton    schedule 21.09.2011
comment
Моя интерпретация этого заключается в том, что вы можете явно превратить что-то в ошибку, но это не значит, что это никогда не будет превращено в ошибку, если вы этого не сделаете. На самом деле я перешел по вашей ссылке и отследил ссылку на Dealloc, и там не сказано строго, что вы не должны переопределять Dealloc, просто в большинстве случаев это, вероятно, неправильно. developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ - person morningstar; 21.09.2011
comment
Core Data не гарантирует, что во всех сценариях (например, при завершении работы приложения) будут вызываться функции Dealloc или Finalize. !!! Я надеюсь, что они просто говорят об исключительных случаях, например, если приложение не выйти через 5 секунд и принудительно завершить работу. Я бы не стал делать в Dealloc ничего подобного тому, против чего они рекомендуют, например, писать файлы, но, может быть, закрывать файлы. developer.apple.com /library/ios/#documentation/Какао/Справочник/ - person morningstar; 22.09.2011