首先,分别来说下分类(category)和扩展(extension)的用处。然后来解读一下分类的底层实现
分类常见的应用
1、可以将臃肿的类根据不同业务划分为多个模块,方便进行管理
2、对原有的类进行扩展,给类添加方法。特别是给系统自带的类方法
分类的注意点
1、分类里面可以定义@property属性,但是不会自动生成set和get方法,以及对应的成员变量
2、分类不能添加成员变量,但是可以通过关联对象的方式添加成员变量
3、分类中有和原有类同名的方法, 会优先调用分类中的方法
4、多个分类中同名方法,优先调用后面参与编译的分类里面的方法
扩展的应用
1、为一个类声明一些额外的方法和属性
2、常用在.m文件中添加一些私有属性。创建viewcontroller文件时会自动在.m文件内最上面实现扩展代码
扩展注意点
1、扩展只能声明方法、属性,具体实现需要在源文件中实现
分类的底层结构
struct category_t {
const char *name;
classref_t cls;
WrappedPtr<method_list_t, PtrauthStrip> instanceMethods;
WrappedPtr<method_list_t, PtrauthStrip> 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);
protocol_list_t *protocolsForMeta(bool isMeta) {
if (isMeta) return nullptr;
else return protocols;
}
};
分类的加载过程
1、通过Runtime加载某个类的所有Category数据
2、把所有Category的方法、属性、协议数据,合并到一个大数组中。后面参与编译的Category数据,会在数组的前面
3、将合并后的分类数据(方法、属性、协议),插入到类原来数据的前面
源码调用顺序
1、_objc_init
2、map_images
3、map_images_nolock
4、_read_images
5、remethodizeClass
6、attachCategories
7、attachLists
8、realloc、memmove、 memcpy
通过分析就知道为什么分类不能添加成员变量,分类同名方法调用顺序
扩展
网上分类文章很多,但是关于扩展的文章较少,而且还都有错误,扩展的底层源码没法分析,下面就来验证一下网上关于扩展的说法
1、不能给系统类添加扩展,关于这个说法尝试一下几步
创建一个iOS工程,给NSObject添加一个扩展NSObject+Extension,编译没问题,运行没问题,所以可以添加扩展文件
在扩展文件中添加属性objName,编译没问题,运行没问题
在控制器中导入扩展的头文件,创建NSObject对象obj,给对象obj.objName赋值,编译没问题,运行报错unrecognized selector sent to instance 0x6000037b4630。找不到对应的方法
创建NSObject分类NSObject+Category,在分类.m文件中导入NSObject+Extension,在分类中使用属性objName。编译没问题,运行报错
在NSObject+Extension中添加方法声明,在分类中实现方法,在控制器中调用方法,编译没问题,运行没问题
总结如下,可以给系统类添加Extension,但是扩展中添加的方法和属性没有意义。
可以再扩展中添加方法声明,在分类中添加具体的方法实现。
在系统类扩展中声明的属性,不会自动生成set方法和get方法。可以再扩展中声明,在分类中添加关联对象。
由此可见,扩展就相当于对类.h的扩展,由于系统类.m文件拿不到,所以对系统类的扩展没有意义,还需要结合分类使用,反而要多导入一个头文件
2、类扩展中声明的方法没被实现,编译器会报警,但是分类中的方法没被实现编译器是不会有任何警告的
这个说法还是不对的
尝试了一下,结果是
扩展中声明的方法没有实现,会在原来的类里面警告
分类中明的方法没有实现,会在分类里面警告
3、非系统类扩展,只有把扩展头文件导入到原类的.m文件中会自动生成set和get方法。扩展声明的方法需要在分类或者元类的.m文件内实现
通过对扩展的分析,又拓展了一个扩展的应用场景,三方库对系统类添加的分类中如果有私有的方法,正常情况是没办法调用的,但是我们可以给这个系统类添加一个拓展,将私有方法在扩展中声明一下就可以调用了
网友评论