分类相信大家都不陌生,经常会用到但是它到底是怎么实现的呢?
- 首先我们先新建一个分类:
//
// SFPerson+Helper.h
// Category
//
// Created by 随风流年 on 2019/8/26.
// Copyright © 2019 随风流年. All rights reserved.
//
#import "SFPerson.h"
NS_ASSUME_NONNULL_BEGIN
@interface SFPerson (Helper)
-(void)run;
@end
NS_ASSUME_NONNULL_END
#import "SFPerson+Helper.h"
@implementation SFPerson (Helper)
-(void)run {
NSLog(@"SFPerson (Helper) run");
}
@end
- 然后利用终端切换到当前文件夹下输入:
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc SFPerson+Helper.m
就可以得到编译后的.cpp
文件得到如下图所示的分类的底层结构:
从上图可以看到分类是一个
category_t 的struct
每一个分类都会生成这样的一个结构体继续查看我们会看到一个变量:
B5832FA14B2395EE9124029BC6C30423.png
在这个结构体变量中又可以查看到我们的方法 1641451F798259ABC9A60DFD18430F5A.png
由此可见分类是生成一个
category_t
的一个结构体,并没有合并到类中
Category编译后的底层结构是 struct category_t,里面存放着分类的对象方法、类方法、属性、协议信息,在程序运行的时候,runtime会将Category的数据,合并到类信息中(类对象、元类对象中)
- 好那么问题来了,它是如何合并的呢?
究其原因:查看苹果源码传送门
查看底层实现:
static void
attachCategories(Class cls, category_list *cats, bool flush_caches)
{
if (!cats) return;
if (PrintReplacedMethods) printReplacements(cls, cats);
bool isMeta = cls->isMetaClass();
// fixme rearrange to remove these intermediate allocations
// 方法数组
method_list_t **mlists = (method_list_t **)
malloc(cats->count * sizeof(*mlists));
//属性数组
property_list_t **proplists = (property_list_t **)
malloc(cats->count * sizeof(*proplists));
// 协议数组
protocol_list_t **protolists = (protocol_list_t **)
malloc(cats->count * sizeof(*protolists));
// Count backwards through cats to get newest categories first
int mcount = 0;
int propcount = 0;
int protocount = 0;
int i = cats->count;
bool fromBundle = NO;
while (i--) { 可以看到是从数字较大的值开始遍历,所以优先编译的优先调用
取出分类中的某个方法
auto& entry = cats->list[i];
取出分类中的对象方法
method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
if (mlist) {
mlists[mcount++] = mlist;
fromBundle |= entry.hi->isBundle();
}
property_list_t *proplist =
entry.cat->propertiesForMeta(isMeta, entry.hi);
if (proplist) {
proplists[propcount++] = proplist;
}
protocol_list_t *protolist = entry.cat->protocols;
if (protolist) {
protolists[protocount++] = protolist;
}
}
取出类中的数据
auto rw = cls->data();
prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
将所有分类的对象方法附加到类对象的方法中
rw->methods.attachLists(mlists, mcount);
free(mlists);
if (flush_caches && mcount > 0) flushCaches(cls);
rw->properties.attachLists(proplists, propcount);
free(proplists);
rw->protocols.attachLists(protolists, protocount);
free(protolists);
}
附加的实现
D65FDC798C1F6C5037D3441B03D731EA.png
- 我们可以看到 附加时,里面 有一个realloc方法重新分配内存,利用oldCount + addedCount 对数组进行扩容。利用
memmove
和memcpy
进行地址的移动和拷贝,将原来列表的位置移动或者拷贝到数组的后面,使分类的列表放在数组前面的位置
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
所以同样的方法会优先调用分类
- 类的扩展也简单说下:
- 类的扩展是(匿名分类\类扩展),它一编译就合并到类中,可用于把属性或者成员变量私有化,外部不可以方法在
.m
中建立@interface SFPerson()
进行声明属性方法
网友评论