分类 (Category)
什么是分类:
分类就是类的补充和扩展部分,补充和扩展的每个部分就是分类。
分类本质上就是类的一部分,分类给特定类添加能力。
分类的定义方式:
主类类名+分类类名
分类文件也分为.h和.m文件
.h文件中存放分类的声明部分
.m文件中存放分类的实现部分
分类的作用:
分类中可以对本类添加额外的方法,比如说:有一个Person的本类,现在分类可以对Person本类添加其余的方法,可以添加eat(),run()等等方法。这样,在不修改本类的情况下,本类多出了eat(),run()等等方法。
怎么创建分类:
Command+N-->Objective-C File-->File Type:Category
分类注意点:
分类中不能添加属性!
分类中不能添加属性!
分类中不能添加属性! —重要的话要说三遍
为什么分类中不能添加属性呢?
因为分类的结构体中根本没有属性列表这一项。
Category
Category 是表示一个指向分类的结构体的指针,其定义如下:
typedef struct objc_category *Category;
struct objc_category {
char *category_name OBJC2_UNAVAILABLE; // 分类名
char *class_name OBJC2_UNAVAILABLE; // 分类所属的类名
struct objc_method_list *instance_methods OBJC2_UNAVAILABLE; // 实例方法列表
struct objc_method_list *class_methods OBJC2_UNAVAILABLE; // 类方法列表
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; // 分类所实现的协议列表
}
如果说非要给分类添加属性呢?
如果非要添加属性,可以。在编译的时候,编译器也不会报错。但是在运行的时候,编译器会崩溃,编译器会说找不到setXX
的方法。其实还是那个问题,分类的结构体中没有属性列表这一项,就算你用了@property
的属性,系统也不会给你自动生成setter
和getter
方法。如果用
.m
文件中强制用@synthesize
来生成setter
和getter
方法呢?
很遗憾,编译器会直接告诉你不可以这样做。哈哈哈,反正就不是不可以。如果我手动去实现
setter
和getter
方法呢?
完全可以。
//运行时实现setter方法
- (void)setNameWithSetterGetter:(NSString *)nameWithSetterGetter {
objc_setAssociatedObject(self, &nameWithSetterGetterKey, nameWithSetterGetter, OBJC_ASSOCIATION_COPY);
}
//运行时实现getter方法
- (NSString *)nameWithSetterGetter {
return objc_getAssociatedObject(self, &nameWithSetterGetterKey);
}
但是,你也只是用点语法来访问这个变量。如果换成用下划线的方式来访问,照样崩溃。
扩展(Extension)
什么是扩展:
扩展是分类的一种特殊形式,扩展是没有名字的分类。有的人也成匿名分类。
扩展通常定义在文件的.m中,不能分开。
扩展可以干什么:
扩展中可以声明:实例变量、属性、方法。并且声明的内容都是私有的,只能在.m文件中使用。
怎么创建扩展:
方式一:
其实,扩展我们每天都在用,算得上我们最熟悉的陌生人了。
比如,你新创建的一个工程。在默认的ViewController文件,.h文件有一个@interface ViewController (),而.m文件也有一个@interface ViewController ()。其实,.m文件这个@interface ViewController ()就是扩展,本类的扩展。自己创建的类,如果没有类的扩展,那么就手动在.m文件里面添加即可。方式二:
Command+N–>Objective-C File–>File Type:Extension(Class对应你的类)–>声明内容–>然后在你原来的类引入这个文件,就直接用刚才声明的内容了。
总结:
- 分类中,原则上只能增加方法,不能添加属性
- 扩展既可以添加属性,也可以添加方法。
- 扩展中声明的方法没被实现,编译器会报警,但是分类中的方法没被实现编译器是不会有任何警告的。这是因为扩展是在编译阶段被添加到类中,而分类是在运行时添加到类中。