在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]
是一个特定的子类,并根据该子类分配属性?