前言
引用 iOS分类(category),类扩展(extension)—史上最全攻略
分类 Category
概念
分类 Catgegory
是 OC
中特有的语法,他表示一个指向分类的结构体指针。原则上,分类只能增加方法,不能添加变量。但是可以通过 runtime
实现变量的添加。
源码
runtime.h
中实现:
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; // 分类所实现的协议列表
}
objc-runtime-new.h
中实现:
struct category_t {
const char *name;
classref_t cls;
struct method_list_t *instanceMethods;
struct method_list_t *classMethods;
struct protocol_list_t *protocols;
struct property_list_t *instanceProperties;
// Fields below this point are not always present on disk.
struct property_list_t *_classProperties;
method_list_t *methodsForMeta(bool isMeta) {
if (isMeta) return classMethods;
else return instanceMethods;
}
property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
};
以上,可以看出,分类中既有实例方法、类方法的存储列表,也有实例属性、类属性的列表。但是变量列表仅仅定义了,并未做返回设置。个人猜测,这也是为啥么添加成员变量,访问会崩溃的原因
注意:
- 分类中无法添加成员变量,即下划线变量。
- 分类中可以写
@property
属性,但是不会生成默认的setter
、getter
方法,也不会生成实现,以及私有的成员变量,编译时警告。 - 可以在分类中访问主类
.h
中的属性、方法,但是无法访问下划线属性。 - 如果分类中右主类同名方法,调用时优先执行分类中的方法。并不是覆盖,以为主类中的方法依然在,只是查找到指定的方法后,就不再继续查找了。方法调用顺序
分类
>主类
>父类
。 - 如果多个分类都有同名方法,则执行顺序由编译器决定。
分类格式
@interface 待扩展的类(分类的名称)
@end
@implementation 待扩展的名称(分类的名称)
@end
举例:
// Programmer+Category.h文件中
@interface Programmer (Category)
@property(nonatomic,copy) NSString *nameWithSetterGetter; //设置setter/getter方法的属性
@property(nonatomic,copy) NSString *nameWithoutSetterGetter; //不设置setter/getter方法的属性(注意是可以写在这,而且编译只会报警告,运行不报错)
- (void) programCategoryMethod; //分类方法
@end
// Programmer+Category.m文件中
下面通过 runtime
实现分类添加属性的功能:
#import <objc/runtime.h>
static NSString *nameWithSetterGetterKey = @"nameWithSetterGetterKey"; //定义一个key值
@implementation Programmer (Category)
//运行时实现setter方法
- (void)setNameWithSetterGetter:(NSString *)nameWithSetterGetter {
objc_setAssociatedObject(self, &nameWithSetterGetterKey, nameWithSetterGetter, OBJC_ASSOCIATION_COPY);
}
//运行时实现getter方法
- (NSString *)nameWithSetterGetter {
return objc_getAssociatedObject(self, &nameWithSetterGetterKey);
}
@end
类扩展 Class Extension
Extension
是 Category
的一个特例。类扩展因为不包含分类名称,又称 匿名分类
。
开发中,我们几乎天天在使用类扩展。
类扩展格式:
@interface XXX ()
//私有属性
//私有方法(如果不实现,编译时会报警,Method definition for 'XXX' not found)
@end
类扩展作用:
- 为一个类添加原来没有的变量、方法和属性
- 一般来说,类扩展写到
.m
文件中 - 一般私有属性写到
.m
文件的类扩展中
分类 VS 扩展
- 分类中原则上只能添加方法。可通过
runtime
添加属性不能添加成员变量。 - 类扩展可以添加属性、方法、成员变量。但默认是
private
类型的。 - 分类中,如果方法、属性没有实现,会有警告。扩展则不会有警告。因为。扩展是在编译阶段加到类中,而类别则是运行时加到类中。
- 扩展没有单独实现的部分,即
@implementation
。需要依赖主类.m
文件实现定义的功能。 -
.h
中类扩展方法、属性是对外共有的,.m
中类扩展中声明的,都是私有的。