关于ios:Objective-C中未申报的方法与类别

Undeclared Methods vs Categories in Objective-C

假设我定义了一个类MyClass,如下所示:

类接口文件:

1
2
3
4
5
6
7
8
9
#import <Foundation/Foundation.h>

@interface MyClass : NSObject

@property (nonatomic) NSString *myProperty;

- (void)myPublicMethod;

@end

使用类别的类实现文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#import"MyClass.h"

@interface MyClass (MyCategory)

- (void)myPrivateMethod;

@end

@implementation MyClass

- (void)myPublicMethod {
    NSLog(@"myPublicMethod was called!");
    [self myPrivateMethod];
}

- (void)myPrivateMethod {
  NSLog(@"myPrivateMethod was called!");
}

@end

不使用类别的替代类实现文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#import"MyClass.h"

@ implementation MyClass

- (void)myPublicMethod {
    NSLog(@"myPublicMethod was called!");
    [self myPrivateMethod];

}

- (void)myPrivateMethod {
    NSLog(@"myPrivateMethod was called!");
}

@end

希望有人能解释这两种实现文件方法之间的区别。

使用categories是否意味着"private"方法由myclass的任何子类继承,不使用categories是否意味着"private"方法不由任何子类继承?


类上存在的所有方法都始终是继承的,并且任何人都可以调用这些方法,而不管您如何声明它们。主要的区别在于是否有人知道他们。还有一个历史性的需求,即在使用前声明内容,这会导致在旧样式的代码中进行内部前向声明。

类别用于向现有类添加方法。一个常见的用途是扩展现有类之一的功能。例如,您可以实现:

1
2
3
4
5
@interface NSURL (HTTPQueryParameters)

- (NSDictionary *)httpQueryParameters;

@end

因此,从那时起,您已经为NSURL本身提供了解析HTTP协议查询参数所需的知识。将功能直接添加到没有源代码的类中通常是正确的分解。

Objective-C用于遵循C规则,即方法只知道编译单元中它们之前的那些方法。因此,为了能够调用在源文件后面出现的方法,您需要一个转发声明。如果您不想发布这个方法让全世界看到,您可以通过一个类别或一个类扩展(为此目的,它只是一个未命名的类别)来实现这一点。

现在,objective-c方法可以调用编译单元中任何地方定义的任何方法,包括随后在同一个源文件中定义的任何方法。因此,为了编译器的利益,现在不将未发布的方法收集到一个类别或扩展中是正常的。

留下的类别有:

  • 向现有类添加功能;以及
  • 如果你的课程变得非常大,就把它们分开;

类扩展现在主要用于:

  • 在不公布的情况下宣布@propertys。

在objective-c中,任何方法调用都可以发送到任何对象,对象是动态类型的。所以在运行时内存中有一个映射表,用于从方法名到实现的每个类。查找过程是查看方法是否在调度到的类中实现。如果没有,那么发送到超类。如果运行时超类用完,将引发异常。


@interface中方法的声明只用于向类的用户公开该方法,包括(正如您在注释中提到的)子类。

(通常使用类扩展(有时称为"匿名类别")声明您在主实现块中定义的方法。实际上,我不能100%地确定类别声明和主块定义之间的交互是什么——如果没有编译,我不会感到惊讶,但确实如此。)

因此,两个示例之间的唯一区别是,声明允许您在希望自己的子类访问此方法,但需要限制框架用户的情况下创建私有头。