OC中的类别、扩展和协议
类别、扩展和协议
[toc]
OC 中的类别(Category)
类别的定义
实现某个类的一部分方法的模块叫做类别。类别可以将有很多方法的超大型的类,分散到不同的模块之中。类别本是 smalltalk 中的概念,用于将多个方法按照相互关系、用途等特征分类,以便最快速的找到自己想要的方法。
调用类别中的方法和调用普通类中的方法是一样的,类别和类一样,都是在接口文件中声明,在类文件中实现,但类别中不能声明实例变量,只能声明方法,声明的方法可以是类方法也可以是实例方法。
类别的声明
@interface 类名(类别名)
方法的声明;
...
@end
类别的定义
@implemention 类名(类别名)
方法的定义;
...
@end
Xcode 中为一个类添加类别的过程:

File 填上类别名
File Type 选择 Category
Class 选择要添加类别的父类

类别的注意事项:
1、只要保证类别名称唯一,我们可以向一个类中添加任意数量的类别;
2、类别中可以添加方法,但不能直接添加变量;
3、类别可以利用运行时功能为已经存在的实例对象增加实例变量;
类别中添加属性的方法:
在类别中添加属性时,只会生成 set 和 get 方法的声明,没有生成 set 和 get 方法的实现。所以如果在类别中添加了属性,直接调用属性的 set 或者 get 是会发生崩溃的。如果有需要在类别中添加属性,可以利用 OC 的 runtime 的特性实现。
Dog + LikeDoing.h
文件
#import <Foundation/Foundation.h>
#import "Dog.h"
@interface Dog (LikeDoing)
@property (nonatomic, copy) NSString *hobby;
- (void) printHobby;
@end
Dog + LikeDoing.m
文件
#import "Dog+LikeDoing.h"
#import <objc/message.h>
@implementation Dog (LikeDoing)
static char *hobbyKey = "hobbyKey";
- (void)setHobby:(NSString *)hobby {
objc_setAssociatedObject(self, hobbyKey, hobby, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)hobby {
return objc_getAssociatedObject(self, hobbyKey);
}
- (void) printHobby {
NSLog(@"%@", self.hobby);
}
@end
OC 中的扩展(Extension)
类扩展的声明
类别只能增加方法,不能直接添加实例变量,但使用类扩展(Class Extension)可以做到。
类扩展也叫匿名类别,不需要指定类别名字。
//add extension
@interface Cat ()
@property (nonatomic, assign) int prices;
//对外不可见
- (void) extensionPrintPrices;
@end
类扩展的实现
一般类扩展都是写在.m文件中,不单独建立一个扩展文件,如下:
#import <Foundation/Foundation.h>
#import "Cat.h"
//add extension
@interface Cat ()
@property (nonatomic, assign) int prices;
//对外不可见
- (void) extensionPrintPrices;
@end
@implementation Cat
- (void)printPrice {
[self extensionPrintPrices];
}
- (void)extensionPrintPrices {
self.prices = 1000;
NSLog(@"%d", self.prices);
}
@end
单独生成一个类扩展文件也可以,但必须将类扩展的.h文件包含进本类的.m文件中。
类扩展的缺陷
1、类扩展中添加的属性、成员变量和方法都属于私有属性和方法(只能被在本类的 .m 文件调用,并且这些属性和方法不能被子类继承);
2、一般类扩展都是写在 .m 文件中,不单独建立一个扩展文件,且必须把类扩展的 @interface 写在本类的 @implementation 的上方,否则编译器会报错;
3、类扩展只能针对自定义的类,不能给系统类增加类扩展;
4、类扩展定义的方法必须在类的实现中进行实现(因为单独定义类扩展的话编译器只会生成一个.h文件,类扩展只能在需要扩展的类.m文件里面去进行扩展);
5、类扩展中声明的方法可以不实现,但编译器会警告(因为类扩展是在编译阶段被添加到类中,而类别是在运行时添加到类中);
6、单独定义类扩展的文件并且只定义属性,也需要在类实现文件中包含进类扩展文件,否则会找不到属性的setter / getter方法。
OC 中的协议(Protocol)
正式协议
OC 中的协议是声明方法的集合体,实现由各个类自行完成,所以实现协议的各个类之间是否有继承关系无关紧要,重要的是这些类如何实现这些协议。
在使用协议的情况下,如果类实现了协议的方法,那么就说类遵循该协议,而该类的子类也因为继承关系拥有这些协议的方法。当类适用于某个协议时,它的实例也适用于这个协议。
下面来看看 protocol 怎么用:
Animal.h
@protocol AnimalDelegate <NSObject>//协议声明
@required
- (void)safeForPeople;
@optional
……
@end
@interface Animal : NSObject
@end
Cat.h
#import "Animal.h"
@interface Cat : Animal<AnimalDelegate>
- (void) printPrice;
@end
Cat.m
@implementation Cat
- (void)safeForPeople{
NSLog(@"Cat can't harm people");
}
@end
main.m
int main() {
Cat *cat = [[Cat alloc] initWeight:11 andHeight:33 andAge:44 andName:@"bey"];
//protocol
[cat safeForPeople];
return 0;
}
一个类可以遵循一个或多个协议,任何类只要遵循了协议就相当于拥有了这个协议中所有的方法声明。Protocol 可以定义在一个类的头文件上部,并直接应用在该类中。
@protocol 是定义一个协议的注解,其中,@required 表示这个方法必须被实现,@optional 表示这个方法不一定要被实现。
AnimalProtocol 是协议的名字,规范命名为:类名 + Delegate。
协议与继承的区别:
- 子类继承父类之后默认就有父类方法的实现,而遵循一个协议只有方法的声明而没有方法的实现;
- 相同类型的类可以使用继承,但是不同类型的类遵循同一个协议更方便;
- 协议可以将多个类中共有的方法抽取出来,让这些类遵守协议
协议与类别的区别:
- 类别是针对类进行扩展,协议可以选择性实现;
- 类别的方法由此类实现,其他类不能重写次此类的方法。协议只定义方法,遵循此协议的类再实现协议中的方法;
- 类别中的方法只能被该类的子类继承,协议可以被不同的类遵循。
非正式协议
苹果官方文档 Cocoa Core Competencies 一文中是这样介绍非正式协议的:
An informal protocol is a category on NSObject
, which implicitly makes almost all objects adopters of the protocol. (A category is a language feature that enables you to add methods to a class without subclassing it.) Implementation of the methods in an informal protocol is optional. Before invoking a method, the calling object checks to see whether the target object implements it. Until optional protocol methods were introduced in Objective-C 2.0, informal protocols were essential to the way Foundation and AppKit classes implemented delegation.
大概意思:非正式协议是 NSObject 类(显而易见,还包括它的子类)的类别,其所有的子类都含蓄地接受了这个协议。(类别是 Objective-C 的一个语言特点,可以让你在无需子类化的前提下为一个类增加方法。)非正式协议中的方法是否实现都是可选的,因此在调用非正式协议中的方法之前,需要去检查对象类是否实现了它。在 Objective-C2.0 中引入可选的正式协议方法之前,非正式协议是 Foundation 和 AppKit 类实现委托的唯一方式。
所以非正式协议就只是基类的类别。