通过类别覆盖Objective c中的方法

时间:2013-01-10 13:53:01

标签: ios objective-c categories

以下是在目标c中工作:

// Base Class in ClassA.h and ClassA.m
@interface ClassA : NSObject 
- (NSString *) myMethod;
@end
@implementation ClassA
- (NSString*) myMethod { return @"A"; }
@end

//Category in ClassA+CategoryB.h and ClassA+CategoryB.m
@interface ClassA (CategoryB) 
- (NSString *) myMethod;
@end
@implementation ClassA (CategoryB)
- (NSString*) myMethod { return @"B"; }
@end

问题是,如果我只是导入ClassA.h并发送消息

[myClassA myMethod]; //returns B

为什么这会回归B?我没有导入ClassA + CategoryB

即便如此,如果我做了以下事情:

// Base Class in ClassA.h and ClassA.m
@interface ClassA : NSObject 
- (NSString *) myMethod;
- (NSString *) mySecondMethod;
@end
@implementation ClassA
- (NSString*) myMethod { return @"A"; }
- (NSString *) mySecondMethod { return [self myMethod]; }
@end

//Category in ClassA+CategoryB.h and ClassA+CategoryB.m
@interface ClassA (CategoryB) 
- (NSString *) myMethod;
@end
@implementation ClassA (CategoryB)
- (NSString*) myMethod { return @"B"; }
@end

并调用mySecondMethod:

ClassA *a = [[ClassA alloc] init];
NSLog(@"%@",[a myMethod]);

结果仍然是B,虽然没有人知道(由于没有导入)类别实现?!

我除外,只是在我导入类别时返回B ...

所以任何提示都会受到赞赏。

3 个答案:

答案 0 :(得分:40)

Objective-C消息传递是动态的,这意味着无论您是否导入类别都无关紧要。该对象将接收消息并执行该方法。

该类别覆盖了您的方法。这意味着当运行时向该对象发送消息时,无论您导入什么内容,都将始终找到重写的方法。

如果要忽略某个类别,则不应编译它,因此可以从编译器源中删除该类别 另一种方法是子类化。

另请阅读:

  

避免使用类别方法名称冲突

     

因为在类别中声明的方法被添加到现有类中,所以需要非常小心方法名称。

     

如果在类别中声明的方法的名称与原始类中的方法相同,或者在同一个类(或甚至是超类)中的另一个类别中的方法相同,则行为未定义为哪个方法实现在运行时使用。如果您使用具有自己类的类别,则不太可能成为问题,但在使用类别向标准Cocoa或Cocoa Touch类添加方法时可能会出现问题。

因此,在您的情况下,您没有遇到任何问题,因为如上所述,用户定义的类不太可能发生这种情况。但是你绝对应该使用子类而不是写一个类别

答案 1 :(得分:10)

Obj-C允许您使用Categories将方法添加到现有类。因此,如果向NSString添加方法,则NSMutableString和所有继承NSString或NSString的子类的类都可以使用分类方法。

但你应该避免覆盖该类别。

您不会100%确定将调用哪种方法。这取决于编译器。

来自Apple文档。

  

尽管Objective-C语言目前允许您使用类别来覆盖类继承的方法,甚至是类接口中声明的方法,但强烈建议您不这样做。类别不能替代子类。使用类别覆盖方法有几个明显的缺点:当类别覆盖继承的方法时,类别中的方法可以像往常一样通过消息调用继承的实现到超级。但是,如果类别覆盖类别类中存在的方法,则无法调用原始实现。类别无法可靠地覆盖在同一类的另一个类别中声明的方法。这个问题特别重要,因为许多Cocoa类都是使用类别实现的。您尝试覆盖的框架定义方法本身可能已在类别中实现,因此未定义优先级高的实现。某些类别方法的存在可能会导致所有框架的行为更改。例如,如果您在NSObject上的类别中覆盖windowWillClose:delegate方法,则程序中的所有窗口委托都会使用category方法进行响应。您所有NSWindow实例的行为可能会发生变化。您在框架类上添加的类别可能会导致行为发生神秘变化并导致崩溃。

答案 2 :(得分:2)

您通过编译隐式地包含了该类别中定义的代码。

如果要避免要执行的类别代码,则应通过删除类别实现文件将其从目标中删除。 你可以从

那里做到

目标 - >构建阶段 - >编译源

那就是说,你应该从不使用类别来覆盖方法。这是一个非常糟糕的做法,并不是什么类别。