Core Data cannot resolve faults when object has "description" attribute?
Asked Answered
M

3

3

Code:

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];

NSEntityDescription *entity = [NSEntityDescription entityForName:@"A"
                                          inManagedObjectContext:moc];
[fetchRequest setEntity:entity];

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"id" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
[sortDescriptors release];
[sortDescriptor release];

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"somePredicate", someObject];
[fetchRequest setPredicate:predicate];

frc = [[NSFetchedResultsController alloc]
       initWithFetchRequest:fetchRequest
       managedObjectContext:moc
       sectionNameKeyPath:@"recency"
       cacheName:@"frc"];
[fetchRequest release];

frc.delegate = self;

NSError *error;
BOOL success = [frc performFetch:&error];
if (!success) {
    NSLog(@"error: %@", error);
}

for (A *a in [frc fetchedObjects]) {        
    [someMutableArray addObject:a.b];
    [someMutableArray addObject:a];
}

Data model:

A and B are entities. A has mandatory to-one relation to B. B has inverse optional to-many relation to A.

Above in English:

Initialize a NSFetchedResultsController to grab some data to power a tableview. After initial fetch, set the data aside for some processing.

Now, later, I try to do this:

id object = [someMutableArray objectAtIndex:someIndex];
NSLog(@"%@", object);

if ([object isMemberOfClass:[B class]]) {
    someVar = object.propertyFromB; // problem
} else if ([object isMemberOfClass:[A class]]) {
    someVar = object.propertyFromA;
}

Question/problem: the line indicated with "problem" crashes. (EDIT: See below for resolution, but would still like an explanation.)

The NSLog call above yields:

2010-01-30 14:47:14.433 app[22618:20b] <B: 0xf7f750> (entity: B; id: 0xf7ba70 <x-coredata://B01FEC86-14D6-4973-BFDB-EDE4AFD24FDC/B/p4> ; data: <fault>)
2010-01-30 14:47:14.438 app[22618:20b] <A: 0xf7e360> (entity: A; id: 0xf35820 <x-coredata://B01FEC86-14D6-4973-BFDB-EDE4AFD24FDC/A/p6> ; data: {
    prop1 = value1;
    prop2 = value2;
    ... etc ...
})

I.e by the problematic line, if the object was of type A, it has been faulted and is available in memory, but if it is B, it's a fault.

My understanding is that the "problem" line should fire the fault and fetch the data from store, but this is not happening. I would like to understand/debug why. I have tried inserting willAccessKey/didAccessKey calls around this. I also tried to set setRelationshipKeyPathsForPrefetching:"b" on the fetch request. Neither worked.

My hypothesis is that since I'm somewhat abusing the NSFetchedRequestController results, the faulting engine gets confused along the way and doesn't fetch the fault when it's supposed to. So I guess a bruteforce way would be to create a new manual fetch request to fetch the related B object at the right time. But is there a better way?

EDIT:

The problem was that object B had a property "description" that I had defined, but that collides with NSObject's built-in name. Xcode always gave me warnings, but I ignored them because I thought "description" internal property/method is only used for dumping strings to console and the like, not internal processing.

The problem disappeared after I made a new version of my model, renaming "description" to something else. All the faulting started to work as expected.

I don't understand, though, what is going on. Is Core Data using the objects' "description" method for some internal introspection?

Molokai answered 30/1, 2010 at 20:4 Comment(0)
A
6

From Core Data Programming Guide

You are discouraged from overriding description—if this method fires a fault during a debugging operation, the results may be unpredictable—and initWithEntity:insertIntoManagedObjectContext:. You should typically not override the key-value coding methods such as valueForKey: and setValue:forKeyPath:.

-description is a method in NSObject that returns a string representation of your object. In the line NSLog(@"%@", object), -description is used to get the string that you see in the console. Key-value coding will end up using the method to get the property for the description attribute. This causes a whole lot of confusion to Core Data.

The programming guide is being generous when it says "discouraged". They really mean "Yeah, it's going to break your stuff."

That link also has a good list of other methods that will break your stuff if you override them.

Adjoining answered 31/1, 2010 at 5:35 Comment(1)
That all makes sense and I generally try to refrain from overriding such "well-known" methods. My bad for not realizing how sensitive "description" was.Molokai
C
0

you need to treat description as a reserved word. That is the issue you are having. You should have received a warning when you tried to have a property called description.

Complexity answered 31/1, 2010 at 7:19 Comment(4)
I did indeed receive a warning. I thought I'd ignore it at the time since I didn't have any problems in the beginning when I started creating the objects. Turns out it was a bad idea :)Molokai
Yeah, ignoring warnings is a bad idea. Every developer should strive for a warning free build! Perhaps even set 'warnings as errors'.Goggin
Is there a best practice for modeling entities that have a "description" attribute? Do people tend to call it "desc" or something?Lepus
Not really a best practice but desc seems to be the most common that I have seen.Complexity
S
0

For custom descriptions you are welcome to override -(NSString *)debugDescription from NSObject Protocol. From Apple's doc:

NSObject implements this method by calling through to the description method. Thus, by default, an object’s debug description is the same as its description. However, you can override debugDescription if you want to decouple these.

Sephira answered 15/1, 2013 at 18:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.