views:

118

answers:

2

I have set up a Core Data model that includes an entity, Item with a 1->M relationship with the abstract entity Place, so that an item has many places. There are several entities with the parent set to Place.

I want to set up several UI elements depending on the descendent place types. I have a loop that looks something like this:

for (Place *place in item.places) {

}

... but I'm not sure how to detect what type the place is, and how to cast it to the proper type so that I can access its properties.

Thanks for any help!

+2  A: 

Not entirely sure what you are asking, but sounds like you have a collection of objects which are of subclasses of Place, and you need to detect the concrete type at runtime.

Here's how you do the branching and casting:

for (id object in item.places) {
    if ([object isKindOfClass:[SomeConcretePlace class]]) {
        ((SomeConcretePlace *)object).someProperty = something;

    } else if ([object isKindOfClass:[AnotherConcretePlace class]]) {
        somethingElse = ((AnotherConcretePlace *)object).someProperty;
    }
}

Be sure to look at docs for isKindOfClass: and isMemberOfClass: in NSObject reference to understand the difference, you can use either depending on the circumstances.

(You can substitute "id object" with "Place *object", I was just using id in my code. -- edit: or maybe you can't if it's abstract, see mzarra's comment. "id" works fine.)

Jaanus
+2  A: 

You can determine what place is with one of several options:

  1. You can call [[object entity] name] to determine the name of the child
  2. If you have subclasses NSManagedObject for these children then you can test against its class using [object isKindOfClass:[XXXX class]]

Since abstract entities can never be instantiated you are best off referencing the results of your query as id or NSManagedObject and then determining which child you are dealing with from there. That way, conceptually, you never get confused as to what you can and cannot instantiate.

Also as an update to this, you do not need to cast the object if you reference it as an id. id is useful as a wildcard in these situations and will be trusted/assumed to accept any message (method call) of any class that the calling class is aware of (i.e. that has been previously imported into the class. This is extremely useful in cases like this where you are dealing with multiple child objects.

Marcus S. Zarra
Casting is useful so that you could directly use the properties the way I've indicated. Without cast, Xcode gives an error if you just try to do object.property if object is "id". With casting, you get nice type completion for properties and all the other goodies.
Jaanus
Casting is only necessary when you are using the toll free bridging from foundation to objective. When you are dealing with 'id' you can call any message on it that the calling object is aware of. This is a core principal behind Objective-C. If you are getting a warning or error then that is because you are not importing the header for the class you are calling. You get type completion without casing as well. Of course if you don't use dot syntax then you have even less issues and more flexibility.
Marcus S. Zarra
Yes, you can call any message on "id". But properties with dot syntax do not seem to be possible without casting. They work fine with id and KVC, but when I try the syntax "objectWithTypeId.property", I get an error without cast, and it works fine with a cast. I have all necessary imports.
Jaanus
then yet another reason to not use dot syntax.
Marcus S. Zarra
You make it sound like there's something wrong with dot syntax? :) What don't you like about it? I find it pretty great over all those square brackets.
Jaanus
There are numerous reasons. The fact that it is turning Objective-C into a heavily type cast language is an abomination on its own. Take a look at Big Nerd Ranch's comments as well as they discuss some of the other issues with it.
Marcus S. Zarra