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

在子类中向下转换Objective-C实例变量,objective-c,class,properties,protocols,subclass,Objective C,Class,Properties,Protocols,Subclass,我有一个类a,其实例变量类型为X @interface A : NSObject { X *x; } @end 一些X实例还符合协议p。我想专门为那些X实例实现一个专门版本的a @interface B : A { X <P> *x; } @end 如果重写实例变量,我必须将更改传播到超类A。这需要额外的功能 使用属性而不是实例变量:这会奏效吗?可以在子类中重新定义属性吗 @interface A : NSObject { X *x; } @proper

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

@interface A : NSObject {
    X *x;
}
@end
一些
X
实例还符合协议
p
。我想专门为那些
X
实例实现一个专门版本的
a

@interface B : A {
    X <P> *x;
}
@end
  • 如果重写实例变量,我必须将更改传播到超类
    A
    。这需要额外的功能

  • 使用属性而不是实例变量:这会奏效吗?可以在子类中重新定义属性吗

    @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
    
    @接口A:NSObject{
    X*X;
    }
    @性质(强,非原子)X*X;
    @结束
    @实施A
    -(X*)X
    {
    返回x;
    }
    -(无效)setX:(X*)anX
    {
    x=anX;
    }
    @结束
    @接口B:A
    @性质(强,非原子)X

    *X; @结束 @实施B @结束


    • 最简单的解决方案是隐藏实例变量。您可以通过将IVAR从接口移动到实现文件中的类延续来实现这一点:

      @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;
      }
      
      -(X*)XTHAT协议
      {
      X*X=super.X;
      返回([x:协议])?x:无;
      }
      
      然而,这种方法会破坏多态性


      老实说,我想你应该用另一种方法来解决这个问题。子类化似乎不是一种自然的选择。

      最简单的解决方案是隐藏实例变量。您可以通过将IVAR从接口移动到实现文件中的类延续来实现这一点:

      @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;
      }
      
      -(X*)XTHAT协议
      {
      X*X=super.X;
      返回([x:协议])?x:无;
      }
      
      然而,这种方法会破坏多态性


      老实说,我想你应该用另一种方法来解决这个问题。子类化似乎不是一种自然的适合。

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

      换句话说,您试图创建一个不是a的a的子类


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

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

      换句话说,您试图创建一个不是a的a的子类


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

      YEP,但是-SETX:的子类实现可能与基类不一样,可以完全不同地对待它。只有
      -x
      方法公开,因为属性在公共头中是只读的。
      -setX:
      的子类实现“以完全不同的方式对待它”正是我所指出的违反LSP的行为。上面的代码片段不会使属性为只读。这没什么区别。子类的约束比其超类的约束窄是没有意义的。子类应该是超类型,具有更广泛的可能操作范围。也许您应该解释真正的类和协议应该是什么,以及为什么要这样做。我怀疑您误用了协议的概念。是的,但是
      -setX:
      的子类实现可能与基类中的不同,并且可能会以完全不同的方式对待它。另外,在我的例子中,只有
      -x
      方法公开,因为属性在公共头中是只读的。
      -setX:
      的子类实现“以完全不同的方式对待它”正是我所指出的违反LSP的行为。上面的代码片段不会使属性为只读。这没什么区别。子类的约束比其超类的约束窄是没有意义的。子类应该是超类型,具有更广泛的可能操作范围。也许您应该解释真正的类和协议应该是什么,以及为什么要这样做。我怀疑你误用了协议的概念。