20

以下是在目标 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在我导入类别时才返回......

所以任何提示表示赞赏。

4

3 回答 3

40

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

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

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

另请阅读:

避免类别方法名称冲突

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

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

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

于 2013-01-10T13:57:30.633 回答
10

Obj-C 允许您使用类别向现有类添加方法。因此,如果您向 NSString 添加方法,则分类方法可用于 NSMutableString 以及继承 NSString 的所有类或 NSString 的任何子类。

但是您应该避免覆盖类别。

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

来自 Apple 文档。

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

于 2013-01-10T14:01:44.323 回答
2

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

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

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

也就是说,您永远不应该使用类别来覆盖方法。这是一个非常糟糕的做法,这不是类别的用途。

于 2013-01-10T14:04:10.170 回答