分类(Category):
分类是OC中的一个特有语法,它表示一个指向分类结构体的指针。一般情况下,我们只能添加给分类增加方法,而不能增加实例变量(成员变量)。
分类的格式:
@interface 待扩展的类(分类的名称)
@end
@implementation 待扩展的类(分类的名称)
@end
分类的使用场景:
我们知道,给一个A类添加方法,简单粗暴的方式就是直接在A类在.h中声明,.m中实现该方法。但是这样做,有时候不便于项目的管理和移植,试想,如果一个类有杂七杂八几十个不同的方法糅合在一个.h中。要是以后我们想要调用其中某一个方法,还得把整个移植过来,或者把方法扣出来。这多恼火。
苹果为了解决这些问题,特地引入了分类这个概念。我们可以专门用一个文件夹管理这些分类。比如UIImage文件夹下面,UIImage+GIF,UIImage+Cache。。。等等。很直观,也更便于项目的管理与移植。
为分类添加属性:
直接给分类添加@property,编译器只会给你警告。但是如果你调用这个属性,就会报错。这是为什么呢?
首先,我们来看看分类的源码组成,源码位于objc/runtime.h中。
struct objc_category {
char * _Nonnull category_name OBJC2_UNAVAILABLE; //分类名称
char * _Nonnull class_name OBJC2_UNAVAILABLE; //要扩展的类名
struct objc_method_list * _Nullable instance_methods OBJC2_UNAVAILABLE; //实例方法列表
struct objc_method_list * _Nullable class_methods OBJC2_UNAVAILABLE; //类方法列表
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE; //分类实现的协议
}
我们发现,分类所指向的结构体中根本就没有属性列表。所以,就算是你强行用@property,编译器也不会给你自动生成setter/getter方法,也不会生成私有成员变量。
但是,有些情况下,我们在给分类添加的方法中,如果有属性存在的话,会便利很多。比如,我想给一个UIButton类扩大点击区域,需要上下左右四个可变数值,用属性的话要便利很多。但如何去给分类添加属性呢?
如果研究过runtime相关的代码,相信你肯定知道关联对象这一说法。如果你暂时还不了解,可以看看我之前的一篇博文
runtime动态关联对象,在此我就只简单描述一下。
定义静态变量,用其地址作为关联对象的key
#import <objc/runtime.h>
@implementation UIButton (EnlargeTouchArea)
static char topNameKey;
static char rightNameKey;
static char bottomNameKey;
static char leftNameKey;
设置关联对象
- (void)addIncreaseTapAreaWithTop:(CGFloat)top buttom:(CGFloat)buttom left:(CGFloat)left right:(CGFloat)right {
objc_setAssociatedObject(self, &kTopNameKey, [NSNumber numberWithFloat:top], OBJC_ASSOCIATION_COPY_NONATOMIC);
objc_setAssociatedObject(self, &kRightNameKey, [NSNumber numberWithFloat:right], OBJC_ASSOCIATION_COPY_NONATOMIC);
objc_setAssociatedObject(self, &kButtomNameKey, [NSNumber numberWithFloat:buttom], OBJC_ASSOCIATION_COPY_NONATOMIC);
objc_setAssociatedObject(self, &kLeftNameKey, [NSNumber numberWithFloat:left], OBJC_ASSOCIATION_COPY_NONATOMIC);
}
获取关联对象
NSNumber *top = objc_getAssociatedObject(self, &kTopNameKey);
NSNumber *right = objc_getAssociatedObject(self, &kRightNameKey);
NSNumber *buttom = objc_getAssociatedObject(self, &kButtomNameKey);
NSNumber *left = objc_getAssociatedObject(self, &kLeftNameKey);
以上,就是如何动态为分类添加属性。
类扩展(Extension):
类扩展其实也是分类的一种,我们应该说是时时刻刻都在打交道。类扩展和分类的区别就在于有没有分类的名称,以及类扩展是编译期属性,分类属于运行时属性。
类扩展的实现:
@interface ExtionsionClass
@property (nonatomic,copy) NSString *name;
- (void)eat;
@end
使用类扩展注意两点:
1.一般类扩展写在依托的对应类的.m中,私有属性写到扩展中。
2.类扩展中方法的实现,需要在依托的对应类的.m中实现(区别于分类,没有@implementation部分)。如果没有实现已经声明了的方法,编译器会报警(区别于分类,分类这种情况不会报警告。这就是我说的编译期和运行时的差别)。
这两种如何合理使用,看各人的爱好以及对应业务的处理吧。