在Objective-C中,如何在保留父类方法的同时完全替换父类的方法';s继承的功能?

在Objective-C中,如何在保留父类方法的同时完全替换父类的方法';s继承的功能?,objective-c,cocoa-touch,cocoa,Objective C,Cocoa Touch,Cocoa,基本上,我有这样一个类层次结构: NSObject MySpecialController MyExtraSpecialController 其中每一个都有一个init方法,每个实现首先调用super,让超类先初始化自己,一直到链的上游。由于缺乏更好的术语,我想说每个类“增强”了它的超级类的行为 但是,让我们假设我想完全“替换”我的超类的行为(仅仅因为我想为特定的应用进一步专门化它,但不干扰通用的可重用超类。因此,假设我对超类有很深的了解)。我要做的实际更改是用更具体的类类型替换属

基本上,我有这样一个类层次结构:

NSObject
  MySpecialController
    MyExtraSpecialController
其中每一个都有一个
init
方法,每个实现首先调用
super
,让超类先初始化自己,一直到链的上游。由于缺乏更好的术语,我想说每个类“增强”了它的超级类的行为

但是,让我们假设我想完全“替换”我的超类的行为(仅仅因为我想为特定的应用进一步专门化它,但不干扰通用的可重用超类。因此,假设我对超类有很深的了解)。我要做的实际更改是用更具体的类类型替换属性。为了完全实现这一点,我需要
init
方法来实例化相应类的
widget
实例。因此,如果我实例化一个
MySpecialController
,它的
widget
属性应该是
MySpecialWidget
类型;但是如果我实例化一个
MyExtraSpecialController
,它的
小部件应该是
MyExtraSpecialWidget
类型:

//MySpecialController:

@interface MySpecialController : NSObject

@property (strong, nonatomic) MySpecialWidget *widget;

@end

@implementation MySpecialController

-(id)init {
    if (self = [super init]) {
        self.widget = [MySpecialWidget new];
    }
}

@end


//MyExtraSpecialController:

@interface MyExtraSpecialController : MySpecialController

@property (strong, nonatomic) MyExtraSpecialWidget *widget;

@end

@implementation MyExtraSpecialController

-(id)init {
    if (self = [super init]) {
        self.widget = [MyExtraSpecialWidget new];
    }
}

@end
现在,它的工作原理是
MySpecialController
工作,任何人都可以使用公共API。而且
MyExtraSpecialController
也可以工作,并遵循适当的关注点分离,因为它对超类的行为没有任何假设。这是一个框架或库类所创建的子类类型:健壮且不张扬

但实际发生的是,当我创建一个新的
MyExtraSpecialController
实例时,它的超类首先实例化一个
MySpecialWidget
,然后它立即释放该实例,并将其替换为
MyExtraSpecialWidget
的实例。当然,这是可行的,但由于我对超类非常熟悉(这基本上意味着我确切地知道它的
init
方法的功能,因此我可以安全地替换它,而无需先调用它),因此我希望避免这个问题,只实例化一个小部件(碰巧创建小部件非常昂贵,而且不是过早的优化).所以我想完全替换super的实现,这样它就不会创建小部件,并且会替换它根据我的个人知识所做的所有其他事情,但是,这是关键,我仍然想调用
init
,因为我不知道被替换的类“superclass”
init
方法做了什么(
NSObject
在本例中),因为这是一个我并不熟悉的类


想到的直接解决方案是使用Objective-C动态运行时来获取祖辈实例,只需调用它的
init
(如果需要,它将负责调用链)但每当我发现自己要做这样的事情时,我总是想知道是否有更好的方法——从概念上讲,即替换而不是扩充超类的方法。有吗?

你可以从
init
函数和im中删除
self.widget
的实例化请改为使用自定义的“惰性”getter函数:

- (MySpecialWidget *)widget
{
     if (_widget == nil) {
         _wigdet = [MySpecialWidget new];
     }
     return _widget;
}

然后您可以在子类中重写此方法。小部件将在首次访问
self.widget
时创建,并调用超类或子类getter。

解决此问题的一个简单方法是创建一个钩子来制作小部件

@implementation MySpecialController

-(id)init {
    if (self = [super init]) {
        self.widget = [self makeWidget];
    }
}

- (MySpecialWidget*) makeWidget
 {
     [MySpecialWidget new];
@end
然后,您的子类可以覆盖
makeWidget
以返回
VerySpecialWidget
。当您不想让客户端知道这些小部件时,这是有意义的

在您的场景中,客户可能知道一些关于小部件的信息,例如,他们想要一个非常特殊的控制器来获得一个非常特殊的小部件。如果是这样,您可能希望让客户选择小部件:

 [MySpecialController initWith: [MyVerySpecialWidget new]];
如果小部件是生成子类的主要力量,那么任何一种方法都可以从一开始就消除生成子类的需要

第二种方法的另一个优点是使单元测试更容易;您可以构建MySpecialController,并无需任何麻烦地向其传递虚拟、存根或模拟:

 [MySpecialController initWith: [MyTestObjectThatPretendsToBeAWidget new]];

但是,如果客户端不应该知道任何关于小部件的信息,那么第一种模式更干净。

一种方法是将实例方法
-widgetClass
添加到
MySpecialController

@implementation MySpecialController

- (id)init
{
    self = [super init];
    if (self) {
        self.widget = [[[self widgetClass] alloc] init];
    }
    return self;
}

- (id)widgetClass
{
    return [MySpecialWidget class];
}

//...

@end
@implementation MyExtraSpecialController

- (id)widgetClass
{
    return [MyExtraSpecialWidget class];
}

//...

@end
并在
MyExtraSpecialController

@implementation MySpecialController

- (id)init
{
    self = [super init];
    if (self) {
        self.widget = [[[self widgetClass] alloc] init];
    }
    return self;
}

- (id)widgetClass
{
    return [MySpecialWidget class];
}

//...

@end
@implementation MyExtraSpecialController

- (id)widgetClass
{
    return [MyExtraSpecialWidget class];
}

//...

@end

这实际上是一个我不知道的有用模式(至少在本文中是这样),谢谢!它并没有完全回答w.r.t替换或扩展超类行为的问题。@lms:是的,它是作为解决您的具体问题的替代方法。我相信会有更多/更好的答案。不能只检查
[self class]
是一个特定的子类,并根据该子类分配属性?