在重写类方法中调用super

时间:2010-12-03 16:35:26

标签: objective-c objective-c-category method-overriding

我想通过类似的类别向UIButtonType课程添加新的自定义UIButton

enum {
    UIButtonTypeMatteWhiteBordered = 0x100
};

@interface UIButton (Custom)

+ (id)buttonWithType:(UIButtonType)buttonType;

@end

是否有可能以某种方式获得该重写方法的super实现?

+ (id)buttonWithType:(UIButtonType)buttonType {
    return [super buttonWithType:buttonType];
}

上述代码无效,因为super在此上下文中引用UIControl

3 个答案:

答案 0 :(得分:7)

您可以在运行时使用自己的自定义方法替换方法,如下所示:

#import <objc/runtime.h>

@implementation UIButton(Custom)

// At runtime this method will be called as buttonWithType:
+ (id)customButtonWithType:(UIButtonType)buttonType 
{
    // ---Add in custom code here---

    // This line at runtime does not go into an infinite loop
    // because it will call the real method instead of ours. 
    return [self customButtonWithType:buttonType];
}

// Swaps our custom implementation with the default one
// +load is called when a class is loaded into the system
+ (void) load
{
    SEL origSel = @selector(buttonWithType:);

    SEL newSel = @selector(customButtonWithType:);

    Class buttonClass = [UIButton class];

    Method origMethod = class_getInstanceMethod(buttonClass, origSel);
    Method newMethod = class_getInstanceMethod(buttonClass, newSel);
    method_exchangeImplementations(origMethod, newMethod);
}

请注意如何使用它,请记住它取代了您的应用使用的每个UIButton的默认实现。此外,它确实覆盖+加载,因此它可能不适用于已经有+ load方法且依赖它的类。

在你的情况下,你可能最好只是继承UIButton。

编辑:正如Tyler在下面所说,因为您必须使用类级方法来创建按钮,这可能是覆盖创建的唯一方法。

答案 1 :(得分:3)

不,当您使用类别来扩充类的功能时,这是不可能的,您不是扩展类,您实际上完全覆盖现有方法,您将丢失原始方法<强>完全即可。像风一样。

如果你创建了UIButton的子类,那么这是完全可能的:

enum {
    UIButtonTypeMatteWhiteBordered = 0x100
};

@interface MyCustomButton : UIButton {}

@end

@implementation MyCustomButton 

+ (id)buttonWithType:(UIButtonType)buttonType {
    return [super buttonWithType:buttonType]; //super here refers to UIButton
}

@end

答案 2 :(得分:3)

雅各布有一个很好的观点,即类别方法与子类方法的行为不同。 Apple强烈建议您只提供全新的类别方法,因为有许多事情可能会出错 - 其中一个定义类别方法基本上会删除同名方法的所有其他现有实现。

不幸的是,对于您要做的事情,UIButton似乎是专门为避免子类化而设计的。获取UIButton实例的唯一受制裁方法是通过构造函数[UIButton buttonWithType:]。像Jacob这样的子类的问题表明(像这样):

@implementation MyCustomButton 

+ (id)buttonWithType:(UIButtonType)buttonType {
  return [super buttonWithType:buttonType]; //super here refers to UIButton
}

@end

[MyCustomButton buttonWithType:]返回的类型仍然是UIButton不是 MyCustomButton。因为Apple没有提供任何UIButton init方法,所以实际上没有一种方法可以让子类实例化自己并正确初始化为UIButton

如果您想要一些自定义行为,您可以创建一个自定义UIView子类,该子类始终包含一个按钮作为子视图,以便您可以利用UIButton的某些功能。

这样的事情:

@interface MyButton : UIView {}

- (void)buttonTapped;

@end

@implementation MyButton

-(id)initWithFrame:(CGRect)frame {
  if (self = [super initWithFrame:frame]) {
    UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
    button.frame = self.bounds;
    [button addTarget:self action:@selector(buttonTapped)
     forControlEvents:UIControlEventTouchUpInside];
    [self addSubview:button];
  }
  return self;
}

- (void)buttonTapped {
  // Respond to a button tap.
}

@end

如果您希望按钮根据更复杂的用户互动执行不同的操作,则可以针对不同的控制事件拨打[UIButton addTarget:action:forControlEvents:]的更多电话。

参考:Apple's UIButton class reference