views:

74

answers:

2

I have a subclass of NSManagedObject (NSMO) called Team. Team has a one to many relationship with another NSMO subclass called Contract. Contract has a 1-to-1 with Player (another NSMO). I would like to simplify my code that uses Team and be able to just refer to its "players". Players will just be an array of players that have a contract with a given team.

I've tried creating a fetched property called players in XCode's data modeling tool with little success. I've tried many different approaches, but the one that makes the most sense is to name it "players", have the destination as Player and finally have as my predicate "contract.team.name == SELF".

In my Team class I have a property of NSArray called players (with is @dynamic players in the implementation). When I NSLog(@"%@", self.players) it logs out...

Relationship fault for (<NSFetchedPropertyDescription: 0x6d19cd0>), name players, isOptional 1, isTransient 1, entity Team, renamingIdentifier players, validation predicates (
), warnings (
), versionHashModifier (null), fetchRequest <NSFetchRequest: 0x6d1a080> (entity: Player; predicate: (contract.team.name == SELF); sortDescriptors: ((null)); type: NSManagedObjectResultType; ) on 0x6d38550

...which makes no sense to me. I feel like I'm doing so many things wrong I don't even know where to begin tackling it.

So I guess my questions are 1) What type should this fetched property assume in my NSMO subclass. (I made it NSArray but apparently it's a NSFetchedPropertyDescription) 2) What code can I use to get an array or set out?

Thanks! Rob

+1  A: 

Can you not just call [aTeam valueForKeyPath:@"contracts.player"]?

Preston
Hi, thanks, that works for my problem. I'd still like to see the answers to the questions though. But what you've given me let's me continue with my project.
rob5408
+3  A: 

Your getting the normal return for a fetched relationship. What you have is a fault i.e. the ghost of an object. The actual managed objects are not loaded in until you try to access one their attributes directly.

Core Data uses faults in relationships to prevent having to load a vast number of objects into memory just to find one small piece of data. Suppose you have a relationship with a 1,000 managed objects on the other side and you need one integer value from one of those managed objects. Without faulting, Core Data would have to load 1,000 objects into memory just to find and retrieve 32-bits of data. With faulting, Core Data knows where the integer value is and can go get that one object with its one value.

You can treat a fetched property just like an array. Take the case of this simple managed object subclass in which theFetchedProperty is a fetched property with a predicate of "TestEntity.order>5"

@interface TestEntityMO :  NSManagedObject  
{}
@property (nonatomic, retain) NSNumber * order;
@property(nonatomic, retain)  NSArray *theFetchedProperty;

Set up like this:

TestEntityMO *testMO;
for (int i=0; i<10; i++) {
    testMO=[NSEntityDescription insertNewObjectForEntityForName:@"TestEntity" inManagedObjectContext:self.managedObjectContext];
    testMO.order=[NSNumber numberWithInt:i];
}

Then log the the last TestEntityMO object created. The first time you log the property, it returns a fault:

NSLog(@"testMO.theFetchedProperty.theFetchedProperty == %@",testMO.theFetchedProperty);

outputs

testMO.theFetchedProperty.theFetchedProperty == Relationship fault for (<NSFetchedPropertyDescription: 0x3d19210>), //... rest removed for brevity

... but you can log the count of the fetched property just like an array:

NSLog(@"[testMO.theFetchedProperty count] == %d",[testMO.theFetchedProperty count] );

... outputs:

[testMO.theFetchedProperty count] == 4

You can get the object at an index just like an array:

NSLog(@"[testMO.theFetchedProperty objectAtIndex:0] == %@",[testMO.theFetchedProperty objectAtIndex:0]);

... outputs the description of a TestEntityMO object (which may or may not be fault. In this case not):

[testMO.theFetchedProperty objectAtIndex:0] == <TestEntityMO: 0x3d20a70> (entity: TestEntity; id: 0x3d20ab0 <x-coredata:///TestEntity/t3A79EE49-39F4-4FCA-8E25-0C28B8E0E01A11> ; data: {
    order = 9;
    theFetchedProperty =     (
        0x3d20ab0 <x-coredata:///TestEntity/t3A79EE49-39F4-4FCA-8E25-0C28B8E0E01A11>,
        0x3d20a00 <x-coredata:///TestEntity/t3A79EE49-39F4-4FCA-8E25-0C28B8E0E01A10>,
        0x3d20880 <x-coredata:///TestEntity/t3A79EE49-39F4-4FCA-8E25-0C28B8E0E01A8>,
        0x3d20970 <x-coredata:///TestEntity/t3A79EE49-39F4-4FCA-8E25-0C28B8E0E01A9>
    );
})

Logging the value of an attribute of a random object:

NSLog(@"[[testMO.theFetchedProperty objectAtIndex:0] order] == %@",[[testMO.theFetchedProperty objectAtIndex:1] order]);

... outputs:

[[testMO.theFetchedProperty objectAtIndex:1] order] == 8

However, if you log the fetched property a second time after you trigger the fetch by the code above you get a actual objects:

NSLog(@"testMO.theFetchedProperty == %@",testMO.theFetchedProperty);

... outputs:

testMO.theFetchedProperty == Relationship objects for (
    <TestEntityMO: 0x3d20a70> ... ; 
data: {
    order = 9;
    theFetchedProperty =     (
        0x3d20ab0 <x-coredata:///TestEntity/t3A79EE49-39F4-4FCA-8E25-0C28B8E0E01A11>,
        0x3d20a00 <x-coredata:///TestEntity/t3A79EE49-39F4-4FCA-8E25-0C28B8E0E01A10>,
        0x3d20880 <x-coredata:///TestEntity/t3A79EE49-39F4-4FCA-8E25-0C28B8E0E01A8>,
        0x3d20970 <x-coredata:///TestEntity/t3A79EE49-39F4-4FCA-8E25-0C28B8E0E01A9>
    );
}),...

In summation you can:

  1. Treat fetched relationships just like NSArrays in code.
  2. As with all managed objects, you will often see a fault if you log the object directly.
TechZen
Wow, thanks! I need to get in and try this.
rob5408
Fantastic answer. Although one question, shouldn't the relationships be treated as NSSet instances not NSArray?
Marcus S. Zarra
@Marcus -- The fetches in fetched properties return arrays so I just left it like that.
TechZen
Ahh right, I was thinking of a normal relationship, ignore my lack of ability to read :)
Marcus S. Zarra