Need some help understanding faulting in Core Data
I have a Core Data entity that uses a custom subclass of NSManagedObject. I have a few instance variables and properties on it that aren't associated with Core Data attributes or relationships. An example of the kind of thing I'm doing is this:
@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
Now in my app I get an instance of Root by a fetch request once when the app launches, and I retain it until the app quits. (Actually it's a bit more complicated because you can delete the root instance and create a new one, but at most one exists at a time and it is retained.) So my hope is that didTurnIntoFault will never be called until my app quits. If it did, then at some point I would refer to root.foo or root.bar and get nil. That would be an error for my app. A Root instance should always have a non-nil value for foo and bar; foo is created whenever the instance is loaded, and bar is se开发者_JAVA百科t by the caller immediately after fetching the root instance.
Can I rely on didTurnIntoFault not being called, if my code is retaining the NSManagedObject?
If I don't want didTurnInfoFault to be called, why do I have it? Well, I have to clean up sometime. Maybe I should really put that code into dealloc, if I don't want those instance variables released until the program quits. But I think I read some documentation that discouraged using dealloc for subclasses of NSManagedObject.
If your object exists in the underlying file store then it can potentially be faulted out at any time due to low memory warnings etc.
But from your code it looks like all you need is a simple lazily-initialized property, i.e. override the 'foo' getter like this:
- (Foo *)foo
{
if (!foo) {
foo = [[Foo alloc] init];
}
return foo; // or [[foo retain] autorelease] if you need that safety
}
This is guaranteed not to return nil, and you can still release and nil out the instance variable if you want in didTurnIntoFault. The next time the getter is called a new ivar will be initialized again.
My interpretation of the documentation is that your object won't be turned back into a fault unless you do it yourself (refreshObject:mergeChanges
and a couple of other methods in the docs: http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CoreData/Articles/cdPerformance.html#//apple_ref/doc/uid/TP40003468-SW4). It also does explicity tell you not to override dealloc, so I would say you're doing the right thing. Would be very interested to hear other thoughts on the topic, though.
精彩评论