分类做了哪些事情
把一些体积庞大的类按功能拆解开放在分类中
声明私有方法
把Framework的私有方法公开
分类添加的方法可以覆盖原来类中的方法,同名的分类方法谁能生效剩下取决于编译顺序,后编译的会覆盖前面的
看一下分类的底层
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);
};
发现分类的结构体里面没有成员变量的数组,这就是为什么分类不能添加成员变量的原因
看一下分类的调用栈,看看分类是怎么合并的
void _objc_init(void)
└──const char *map_2_images(...)
└──const char *map_images_nolock(...)
└──void _read_images(header_info **hList, uint32_t hCount)
└──static void remethodizeClass(Class cls)
└──static void attachCategories(Class cls, category_list *cats, bool flush_caches)
void _objc_init(void)
{
//省略掉非关键代码
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
}
void map_images(unsigned count, const char * const paths[],
const struct mach_header * const mhdrs[])
{
rwlock_writer_t lock(runtimeLock);
return map_images_nolock(count, paths, mhdrs);
}
map_images_nolock(unsigned mhCount, const char * const mhPaths[],
const struct mach_header * const mhdrs[]) {
//省略掉非关键代码
_read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
}
void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses)
{
remethodizeClass(cls);
}
static void remethodizeClass(Class cls)
{
category_list *cats;
bool isMeta;
runtimeLock.assertWriting();
isMeta = cls->isMetaClass();
// Re-methodizing: check for more categories
if ((cats = unattachedCategoriesForClass(cls, false/*not realizing*/))) {
if (PrintConnecting) {
_objc_inform("CLASS: attaching categories to class '%s' %s",
cls->nameForLogging(), isMeta ? "(meta)" : "");
}
attachCategories(cls, cats, true /*flush caches*/);
free(cats);
}
}
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();
repareMethodLists(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);
}
void attachLists(List* const * addedLists, uint32_t addedCount) {
if (addedCount == 0) return;
if (hasArray()) {
// many lists -> many lists
uint32_t oldCount = array()->count;
uint32_t newCount = oldCount + addedCount;
//重新分配数组的大小
setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
array()->count = newCount;
//进行内存移动 把原来的数组里面的数据往后移动了addedCount个大小的位置
memmove(array()->lists + addedCount, array()->lists,
oldCount * sizeof(array()->lists[0]));
//再把分类二维数组里面的方法列表copy到这个扩容的数组里面来
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
}
else if (!list && addedCount == 1) {
// 0 lists -> 1 list
list = addedLists[0];
}
else {
// 1 list -> many lists
List* oldList = list;
uint32_t oldCount = oldList ? 1 : 0;
uint32_t newCount = oldCount + addedCount;
setArray((array_t *)malloc(array_t::byteSize(newCount)));
array()->count = newCount;
if (oldList) array()->lists[addedCount] = oldList;
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
}
}
总结一下attachCategories代码思路,以合并实例方法为主
创建一个二维数组 method_list_t **mlists = (method_list_t *) malloc(cats->count * sizeof(mlists))
获得所有分类的数量int i = cats->count;
倒叙遍历while (i--)(后来编译的会放在数组前面)
获得i所对应的的实例方法列表数组method_list_t *mlist = entry.cat->methodsForMeta(NO);
然后把method_list_t放在mlists二维数组里面,是直接把一维数组放在二维数组里面,也就是说不同分类之间的方法列表是分开的
下面的类方法、协议和属性列表的合并是一样的
根据(bits & FAST_DATA_MASK)获得class_rw_t
然后class_rw_t得到里面的methods二维数组,进行数组合并rw->methods.attachLists(mlists, mcount);
attachLists里面的思路
oldCount是class_rw_t里面methods的数量
newCount是oldCount加上分类的数量
重新分配数组的大小setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
再把分类二维数组里面的方法列表copy到这个扩容的数组里面来memcpy(array()->lists, addedLists, addedCount * sizeof(array()->lists[0]));
网友评论