Walking Core Data graph to-many relationships always seems awkward, is there a better way?
I'm able to get things working fine with Core Data and to achieve my desired result开发者_开发技巧s, but I always feel it very awkward when walking to-many relationships because NSSet is, for my typical purposes, fairly useless.
An example is if I have obtained a NSManagedObject of Entity "Zoo" with attribute "nameOfZoo" and to-many relationship "animalCages", the to-many relationship pointing to Entity "AnimalCage" which has attribute "nameOfSpecies" and to-many relationship pointing to Entity "IndividualAnimal"
Zoo [nameOfZoo] ->> AnimalCage [nameOfSpecies] ->> Animals
So, getting the top level Zoo object, that's simple. But then I want to get the data for nameOfSpecies "Canus Lupus". The code I want to write is this:
// Normal NSEntityRequest or whichever it is, I have no gripe with this
NSManagedObject *zoo = ..the request to get the one Zoo..;
// I want to get the object where the key "nameOfSpecies" is set to "CanusLupus"
NSManagedObject *wolf = [[zoo animalCages] object:@"Canus Lupus" forKey:@"nameOfSpecies"];
Obviously, I can't obtain wolf in this manner. Instead, I have to write like 10 lines of code (feels like 100 lines of code) to first obtain the set, then set up a search predicate request, and declare an error variable, execute the request, then get an array of the results, then get the first element of that array.. and if I want to walk further down the tree, to find the animal named "Wolfy" for instance, then I have to do it all over again.
Am I doing things correctly or am I foolishly an easier way? I guess I can put a category on NSSet, maybe I will, but I feel like there should be a built in better way. If not, why?
If you have a data model like this:
Zoo{
name:string
animalCages<-->>AnimalCage.zoo
}
AnimalCage{
nameOfSpecies:string
zoo<<-->Zoo.animalCages
animals<-->>Animal.animalCage
}
Animal{
name:string
animalCage<<-->AnimalCage.animals
}
The to find a specific AnimalCage
by name of species for a given Zoo
object:
NSString *soughtSpecies=@"Canis Lupis"; // normally this variable would be passed in to the method
NSPredicate *p=[NSPredicate predicateWithFormat:@"nameOfSpecies==%@", soughtSpecies];
NSSet *wolves=[[zoo animalCages] filteredSetUsingPredicate:p];
Or you can use objectPassingTest:
if you like blocks.
If you use custom NSManagedObject subclasses, then you get custom accessor methods and can use self.dot notation so the above would be:
Zoo *zoo= // fetch the appropriate zoo object
NSString *soughtSpecies=@"Canis Lupis";
NSPredicate *p=[NSPredicate predicateWithFormat:@"nameOfSpecies==%@", soughtSpecies];
NSSet *wolves=[zoo.animalCages filteredSetUsingPredicate:p];
If you know before hand that you are going to have to find cages a lot, you could wrap the above up in a method on your Zoo
class e.g.
@implementation Zoo
//... other stuff
-(AnimalCage *) cageForSpecieNamed:(NSString *) specieName{
NSPredicate *p=[NSPredicate predicateWithFormat:@"nameOfSpecies==%@", specieName];
NSSet *wolves=[self.animalCages filteredSetUsingPredicate:p];
return [wolves anyObject]; // assuming one cage per species
}
Objective-c is intentionally verbose because it was supposed to be "self documenting" and the editor written for the language at the beginning had autocomplete. So, if your used to a more expressive language it might seem you are doing a lot of work but logically you aren't.
精彩评论