8

I would like to cast dynamically in Objective C and access instance properties. Here a pseudo code:

id obj;
if (condition1)
    obj = (Class1*)[_fetchedResults objectAtIndex:indexPath.row];
else
    obj = (Class2*)[_fetchedResults objectAtIndex:indexPath.row];

NSNumber *latitude = obj.latitude;

Then the compiler tells me the following: property 'latitude' not found on object of type '__strong id'

Either Class1 and Class2 are core data entities and have nearly the same kind of attributes. In condition1 _fetchedResults returns objects of type Class1 and in condition2 _fetchedResults returns objects of type Class2.

Could someone give me a hint how to solve this kind of problem?

Thanks!

DoK
  • 851
  • 3
  • 15
  • 31
  • I think you should make sure firstly that what [_fetchedResults objectAtIndex:indexPath.row] returns is the Class has the latitude property. Also, casting a none NSManagedObject type to a NSManagedObject type is wrong either. You need a NSManagedObjectContext whenever and however you create a NSManagedObject instance. – Owen Zhao Aug 07 '14 at 07:48

2 Answers2

7

You can access the properties through Key-Value Coding (KVC):

[obj valueForKey:@"latitude"]
Monolo
  • 18,205
  • 17
  • 69
  • 103
  • Note that using KVC to do this robs you of any type-checking from the compiler. – ipmcc Apr 30 '13 at 12:14
  • 6
    @ipmcc Sure, but so does using `id` in the first place :-) – Monolo Apr 30 '13 at 12:15
  • @Monolo This is unnecessary and has the same effect as [obj latitude] – hooleyhoop Apr 30 '13 at 12:21
  • @hooleyhoop True. I'd venture that using KVC conveys intention slightly better, but that is a matter of coding style. The error messages you'd get (at runtime, alas) are also quite different. And you might in theory have defined a getter with a different name if we want to cover that one, too. – Monolo Apr 30 '13 at 12:24
  • Key-Value coding is documented practice for managed objects, so the only thing I would change is to replace `id` by `NSManagedObject *`. – Martin R Apr 30 '13 at 12:27
3

The obj variable needs to be of a type that has the property in question. If both entities have the same property, one way to achieve this would be for the property to be declared on a common base class. If it's not appropriate for these two types to share a common base class, then you could have them adopt a common protocol, like this:

@protocol LatitudeHaving
@property (copy) NSNumber* latitude;
@end

@interface Class1 (AdoptLatitudeHaving) <LatitudeHaving>
@end

@interface Class2 (AdoptLatitudeHaving) <LatitudeHaving>
@end

From there, you would declare obj as being an id<LatitutdeHaving>, like this:

id<LatitudeHaving> obj;
if (condition1)
    obj = (Class1*)[_fetchedResults objectAtIndex:indexPath.row];
else
    obj = (Class2*)[_fetchedResults objectAtIndex:indexPath.row];

NSNumber *latitude = obj.latitude;

And that should do it. FWIW, protocols are similar to Interfaces in Java.

ipmcc
  • 29,581
  • 5
  • 84
  • 147
  • what are the Class1* and Class2* casts for? – hooleyhoop Apr 30 '13 at 12:22
  • `objectAtIndex:` returns an `id` not an `id`. Those casts may not be strictly necessary to avoid compiler complaints, but I was trying to make the code as close to the OP's code as possible. – ipmcc Apr 30 '13 at 16:55