问题描述
今天我遇到了一个我不希望Objective-C编译器允许的奇怪行为。
Today I came across a strange behavior that I did not expect Objective-C compiler would allow.
在UITableViewCell中,有一个名为imageView的属性为UIImageView。我将UITableViewCell子类化,并覆盖了imageView,除了我的AWImageView类型,其中AWImageView是UIImageView的子类。我认为它不会编译,但确实如此。一切正常。我对这种行为感到非常震惊。
In UITableViewCell, there is a property named imageView of the type UIImageView. I subclassed UITableViewCell, and overwrote imageView, except I made it of the type AWImageView where AWImageView is a subclass of UIImageView. I thought it would not compile, but it does. Everything works just fine. I was very much shocked by the behavior.
正在缩小正式允许的子类中的属性类型?或者这是Objective-C编译器中的一个错误吗?
Is narrowing the type of property in subclass allowed officially? Or is this a bug in Objective-C compiler that made it work?
推荐答案
你怀疑被允许这样做很好成立,但在这个特定的情况下,你没事......
Your suspicion of being allowed to do this is well founded, but in this particular case you are OK...
在重写的严格子类型解释中,一个重写方法可能接受更一般的类型并返回更具体类型的值。
In a strict subtyping interpretation of overriding, an overriding method may accept arguments of more general type and return a value of more specific type.
例如,使用Objective-C,给定:
For example, using Objective-C, given:
@interface A : NSObject { ... }
@interface B : A { ... }
@interface C : B { ... }
和B中的方法M:
- (B *) M:(B *)arg { ... }
然后在严格子类型下的C类中,可以使用以下方法在C类中覆盖:
then in class C under strict subtyping this could be overridden in class C using:
- (C *) M:(A *)arg { ... }
这是安全的,因为如果你有一个参考显然是B对象:
This is safe because if you have a reference to an apparently B object:
B *bObj = ...;
然后调用方法M:
B *anotherBObj = [bObj M:[B new]];
然后 bObj
实际上是B还是一个C调用类型是正确的 - 如果它是一个C对象那么参数是B是好的,因为它也是A,结果是C很好,因为它也是B。
then whether bObj
is actually a B or a C the call is type correct - if it is a C object then the argument being a B is fine as it's also an A, and the result being a C is fine as it's also a B.
这使我们,不完全,你的财产;在Objective-C中,属性只是两种方法的简写:
Which brings us to, not quite, your property; in Objective-C a property is just a shorthand for two methods:
@property B *myBvalue;
是简写:
- (void) setMyBvalue:(B *)value;
- (B *) myBvalue;
如果该属性在B中声明,并且您在C类中使用C值属性覆盖它:
If that property is declared in B and your override it in class C with a C-valued property:
@property C *myBvalue;
你得到:
- (void) setMyBvalue:(C *)value;
- (C *) myBvalue;
且方法 setMyBvalue:
违反严格规定子类型规则 - 将一个C实例强制转换为一个B实例,并且输入规则说你可以传递一个B,该方法需要一个C,然后就会出现混乱。
and the method setMyBvalue:
violates the strict subtyping rule - cast a C instance to a B instance and the typing rules say you can pass a B, the method expects a C, and chaos can ensue.
但是在您的情况下,您覆盖的属性是 readonly
,因此没有setter,也没有危险。
However in your case the property you are overriding is readonly
, so there is no setter, and no danger.
这篇关于子类中的窄类型属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!