tags:

views:

46

answers:

3

Hi everyone,

I have two classes (class1 and class2) that just store data, no methods. I have a third class that has an instance variable that, depending on some user input, will be set to one of the two classes. So, in the third class I declare the variable generically as

NSObject *aClass;

and during runtime set it to whatever it should be.

aClass = [[Class1 alloc] init]; // or 
aClass = [[Class2 alloc] init];

However, when I try to access fields from aClass

NSString *str = aClass.field1;

It gives me the error: request for member 'field1' in something not a structure or a union. Field1 is declared in both class1 and class2. When I try to cast aClass

aClass = (Class1 *) aClass;

it gives the same error. What am I doing wrong, is there a better way to do this?

+4  A: 

It's because properties only work when the compiler knows the class it has to deal with. NSObject has no property called field1, so it doesn't work.

You can, however, use the field1 method the property implicitly created:

NSString* str = [aClass field1];

But if you're doing it on a NSObject, it's going to emit a warning.

If you don't know which of the two classes you will have and they don't have any class relationship, I suggest you use the id type instead of NSObject. Using id, the compiler won't emit warnings about method calls that could potentially fail (but still won't resolve your properties).

zneak
Thanks, I didn't realize there was a difference between using dot notation and property method regarding this issue. To get it straight though, aClass.field accesses the field directly, meaning no method was called, while [aClass field] calls a method?
Moses
@Moses: unfortunately, the behavior is less consistent than that. Objects in ObjC are pointers; you'd need the `->` operator to access fields directly. I don't know why properties fail when the type can't be determined at compile-time: getting or setting a property is indeed a method call, and simply not using the dot syntax for the same job works perfectly. In other words, both `aClass.field` and `[aClass field]` should do the same thing; except the first only works when the compiler knows the type of the object.
zneak
+1  A: 

All the compiler knows about aClass is that its an NSObject, so you can only treat it as such. Otherwise, what should happen if you try to access field1 when aClass points to an NSString? Make a superclass with the instance variables you want and declare aClass to be of that type. That ensures that aClass will contain a field1 variable so you can carry on without the poor compiler getting confused.

The reason you can't cast down from NSObject to Class1 follows the same principle. aClass could be anything, and the compiler doesn't know how to anything to a Class1. You can cast the other way because all aClass are NSObjects, but not all NSObjects are aClass. :D

CrazyJugglerDrummer
Objective-C's not typesafe and everything is resolved at runtime. If you try to access `field1` when aClass points to an `NSString`, it should throw the same exception it throws when you try to use an undefined method. I have to agree that it's weird you can call any method on any object and it will work at compile-time, but you can't use any property on any object.
zneak
Thanks for the explanation about casting. One quick question though. Just as if I were to declare Class1 *aClass = [[Class1 alloc] init] and then try to access a field by calling aClass.field1, the compiler wouldn't complain, why would it complain if aClass is declared as an NSObject and then casted into a Class1? When it checks to see if the property exists, doesn't it do it based on type?
Moses
A: 

Why not have both of the possible classes inherit from one base class that implements the property you are trying to define?

Or, create a category with that property that both classes implement, and instead of defining an NSObject variable say:

id <MyClassCategory> aClass;
Kendall Helmstetter Gelner