1

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

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

一些X实例还符合协议P。我想A为这些X实例实现一个专门的版本。

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

这编译得很好。但是,当我xB实例访问时,更改不会传播到A. 因此,A未重新定义的函数使用X的函数以外的其他实例B

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

  • 如果不可能:是否有执行此操作的通用模式?

    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
      
4

2 回答 2

1

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

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

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

于 2012-06-06T13:51:24.703 回答
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;
}

但是,这种方法会破坏多态性。

老实说,我认为您会使用不同的方法(可能是组合)来解决这个问题。子类化似乎不是很自然的选择。

于 2012-06-06T11:53:22.553 回答