在子类中向下转换Objective-C实例变量

时间:2012-06-06 11:17:01

标签: objective-c class properties protocols subclass

我有一个类A,其实例变量类型为X

@interface A : NSObject {
    X *x;
}
@end

某些X个实例另外符合协议P。我想专门为那些A实例实现X的专用版本。

@interface B : A {
    X <P> *x;
}
@end

编译好。但是,当我从x实例访问B时,更改不会传播到A。因此,未重新定义的A函数使用的是除X函数之外的B个实例。

  • 在仅添加协议时,是否有可能重新定义实例变量? 关于子类有类似的question。但是,子类和协议不一样。虽然我的方法编译得很好,但它不能按预期工作。

  • 如果不可能:是否有共同的模式来执行此操作?

    1. 如果我没有用新类型覆盖实例变量,那么代码就会变得杂乱无章:

      [((X <P> *)x) methodOfP]
      
    2. 如果我覆盖实例变量,我必须自己将更改传播到超类A。这需要额外的功能。

    3. 使用属性而不是实例变量:这甚至可以解决吗?可以在子类中重新定义属性吗?

      @interface A : NSObject {
          X *x;
      }
      @property (strong, nonatomic) X *x;
      @end
      @implementation A
      - (X *)x
      {
          return x;
      }
      - (void)setX:(X *)anX
      {
          x = anX;
      }
      @end
      
      @interface B : A
      @property (strong, nonatomic) X <P> *x;
      @end
      @implementation B
      @end
      

2 个答案:

答案 0 :(得分:1)

您尝试做的事情违反了面向对象编程的基本原则Liskov Substitution Principle。可以将B的实例传递给任何期望A的代码,并且该代码有权将其视为A.这意味着允许在对象上调用-setX:,传入X 的实例而不任何要求该对象符合协议P

换句话说,您正在尝试创建A的子类,而不是A。

您想要这样做的事实表明设计混乱。你应该退一步重新思考。首先,仅根据接口设计所有类,并确保它们的接口在继承方面有意义。只有这样才能考虑实施。

答案 1 :(得分:0)

最简单的解决方案是隐藏实例变量。您可以通过将ivars从接口移动到实现文件中的类继续来完成此操作:

@interface A ()
{
    X *x;
}
@end

或执行的负责人:

@implementation A 
{
    X *x;
}
//methods go here
@end

您不应直接访问超类ivar。而是使用super.propertyName。然后,您可以向子类添加一个方法,只有在它实现协议时才返回x

-(X*<protocol>)xThatImplementProtocol
{
     X *x = super.x;

    return ([x conformsToProtocol:protocol]) ? x : nil;
}

然而,这种方法会破坏多态性。

说实话,我认为你会使用不同的方法,可能是合成,来解决这个问题。子类化似乎不太合适。