NSObject的类别可用于每个NSObject子类,即使没有在任何地方导入此类别的.h文件

时间:2013-08-27 01:30:43

标签: ios objective-c objective-c-category

背景

请考虑以下步骤:

1)在Xcode中创建一个新的“单一视图应用程序”。

2)创建一个类别NSObject + Extension.h和.m文件:

// .h
@interface NSObject (Extension)
- (void)someMethod;
@end

// .m
@implementation NSObject (Extension)
- (void)someMethod {
    NSLog(@"someMethod was called");
}
@end

3)确保NSObject+Extension.m文件包含在主目标中。

4)将以下行添加到AppDelegate:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    [[NSString new] performSelector:@selector(someMethod)];

    return YES;
}

5)确保应用中的#import "NSObject+Extension.h不存在

6)运行应用程序。

输出

2013-08-27 04:12:53.642 Experimental[32263:c07] someMethod was called

问题

  1. 我想知道在应用程序的任何地方是否没有此类别的#import,甚至可能 NSString仍然可以使用NSObject + Extension吗?这种行为让我对我声明的每个Objective-C类别都感觉非常糟糕,因为我希望我声明的类别只在声明的范围内 。例如,我希望NSObject仅在某些类中被Extension扩展,而不是在整个应用程序中扩展,因为否则其全局空间会变为“污染”。

  2. 有没有办法避免这种行为?我确实希望我的类别仅在我明确导入它们时才能工作,而不是在我将它们链接到我用来运行的目标时。

2 个答案:

答案 0 :(得分:14)

  

我想知道在应用程序的任何地方是否没有此类别的#import,NSString如何仍然可以使用NSObject + Extension?这种行为让我对我声明的每个Objective-C类别感觉非常糟糕,因为我希望我声明的类别仅在它们声明的范围内可用。例如,我希望NSObject只能在某个类中扩展,但不能在整个应用程序中扩展,因为否则其全局空间会变为“污染”。

Objective-C对象上没有名称空间。如果您声明某个类有方法(无论是通过类别还是主要@interface),那么该类的每个实例都将具有该方法。

Objective-C处理“私有”方法的方式是选择不告诉其他人有关方法(这是通过#import - 声明这些方法的文件来完成的。这与-Wundeclared-selector一起使用(如果你使用编译器不知道的选择器就会发出警告)就像你将要获得的那样一样好。

但是无论如何,如果你将.m文件编译成你的最终二进制文件,那么方法存在,即使没有其他人“知道”它。

  

有没有办法避免这种行为?我确实希望我的类别仅在我明确导入它们时才起作用,而不仅仅是当我将它们链接到我用来运行的目标时。

是的,使用-Wundeclared-selector,Xcode会警告你。

答案 1 :(得分:4)

包含标题只是让编译器知道它。它编译它,因为xCode编译目标中包含的每个文件。在运行时,该方法将存在,因此即使您没有将其包含在编译时检查中,该对象仍将响应该类别方法。