Subclasses can change types associated with methods. In general, a subclass may specialize a return type, and may make argument types more generic. There's actually a name for this but I can't remember what it is. Anyway, here's the rational:
Return types
If I have a class
@interface A
- (id)foo;
@end
and another class
@interface B : A
- (NSString *)foo;
@end
And I have an instance B* b
, I can cast it down to A*
and still conform to the type signature of the method -[A foo]
, because any NSString*
is also an id
.
However, I cannot make this more generalized. If instead I have
@interface A
- (NSString *)foo;
@end
@interface B : A
- (id)foo;
@end
And I have an instance B* b
and I cast it down to A*
, then the type of [(A*)b foo]
is NSString *
and yet the actual value may be any id
, because that's the type I declared -[B foo]
to be. This is a violation of the type system.
Arguments
If I have a class
@interface A
- (void)foo:(NSString *)obj;
@end
and another class
@interface B : A
- (void)foo:(id)obj;
@end
And I have an instance B* b
and I cast it down to A*
, then any valid argument to [(A*)b foo:obj]
also conforms to the type of -[B foo:]
, because any NSString *
is also an id
.
However if I have the following
@interface A
- (void)foo:(id)obj;
@end
@interface B : A
- (void)foo:(NSString *)obj;
@end
And I have an instance B* b
and I cast it down to A*
, then I could pass any id
to [(A*)b foo:obj]
, but the underlying class B
only expects NSString*
s. And thus I've violated the type system.
Properties
Here is the sticky point. When you declare the type of a property, you're declaring both the return type of the getter and the argument type of the setter. According to the above rules, this means you cannot change the type of a property, because in one of the two cases you'll be violating the type system.
The above is the theory. In practice, I have no idea if GCC or Clang enforce these constraints. It's possible that they assume the programmer knows best, and improperly generalizing or specializing a type will silently break the type system behind your back. You'll have to experiment. But if the compiler is truly correct then it will disallow generalizing return types and specializing arguments. And that means it will disallow changing the type of a property.
Even if the compiler allows it, you probably shouldn't do it. Silently breaking the type system is a great way to introduce bugs, and an indicator of poor architecture.
((Base *)myDerived).obj = [NSNumber numberWithInt:3]
. – Plebe(UICollectionViewController *)myDerived
and then... i.e., if you are casting something to a type that it is not, don't expect good results. This includes not being able to treat a subclass as though it was its superclass. – Zendah