分类(Category
)是OC中的特有语法,它是表示一个指向分类的结构体的指针。原则上它只能增加方法,不能增加成员(实例)变量。
Category的作用
为已经存在的类添加方法。可以在不修改原来类的基础上,为一个类 扩展方法。最主要的应用:给系统自带的类 扩展方法。
- 注意
1.分类中只能添加“方法”,不能增加成员变量。分类中的可以写@property
, 但不会生成setter/getter
方法, 也不会生成实现以及私有的成员变量,会编译通过,但是引用变量会报错。
2.分类中可以/只能
访问原有类中.h中的属性。如果想要访问本类中的私有变量,分类和子类一样,只能通过方法来访问。
3.在本类和分类有同名方法时,优先调用分类的方法
。同名方法调用的优先级为分类 > 本类 > 父类
。
4.如果多个分类中都有和原有类中同名的方法,那么调用该方法的时候执行谁由编译器决定;编译器会执行最后一个参与编译
的分类中的方法。
分类方法不会覆盖掉原来类中的方法,而是共存的。但是分类中的方法在前面,原来的类中的方法在后面,调用的时候,就会调用分类中的方法,如果多个分类有同样的方法,后编译的分类会调用。
创建分类
例如,创建一个UIView的分类:
在新建的文件的.h中声明新方法,在.m中具体实现方法
格式:
@interface 待扩展类的类名(分类名字)
/*方法声明*/
@end
@implementation 待扩展类的类名(分类名字)
/*方法实现*/
@end
底层原理
编译完之后每一个分类都会生成一个结构体category_t
,里面存储着分类的对象方法、类方法、属性、协议信息。在程序运行的时候,runtime会将Category的数据,合并到类信息中(类对象、元类对象中)
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);
};
案例
UIImage的分类,添加扩展方法更改图片大小。
.h中
#import <UIKit/UIKit.h>
@interface UIImage (Size)
- (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize ;
@end
.m中
#import "UIImage+Size.h"
@implementation UIImage (Size)
- (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize {
UIGraphicsBeginImageContext(newSize);
[image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
@end
分类(Category)和类扩展(extension)的关系
1.类扩展(extension
)是Category
的一个特例,有时候也被称为匿名分类。他的作用是为一个类添加一些私有的成员变量和方法。
2.和分类不同,类扩展即可以声明成员变量又可以声明方法。
3.分类的小括号中必须有名字
Class Extension
在编译的时候,它的数据就已经包含在类信息中。
Category
是在运行时,才会将数据合并到类信息中。
其他
- Category中有load方法吗?
有load方法,load方法在runtime加载类、分类的时候调用。load方法可以继承,但是一般情况下不会主动去调用load方法,都是让系统自动调用。