Cateogry

Category的数据结构

============================== 查看runtime源码如下 ==============================
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);
    
    protocol_list_t *protocolsForMeta(bool isMeta) {
        if (isMeta) return nullptr;
        else return protocols;
    }
};

Category的加载处理过程

1、通过Runtime加载某个类的所有Category数据

2、把所有Category的方法、属性、协议数据,合并到一个大数组中

2.1、后面参与编译的Category数据,会在数组的前面

3、将合并后的分类数据(方法、属性、协议)插入到类原来的数据前面

============================== 建立一个类和它的分类 ==============================
@interface TBPerson : NSObject

@end

@implementation TBPerson

@end

@interface TBPerson (Learn) <NSCopying,NSCoding>

@property (nonatomic, strong) NSString *name;

- (void)testOne;

+ (void)testTwo;

@end

@implementation TBPerson (Learn)

- (void)testOne{
}

+ (void)testTwo{
}

@end
============================== 查看转换为C语言代码后的底层实现 ==============================

//	分类的信息存放在这个结构体中
struct _category_t {
	const char *name;
	struct _class_t *cls;
	const struct _method_list_t *instance_methods;
	const struct _method_list_t *class_methods;
	const struct _protocol_list_t *protocols;
	const struct _prop_list_t *properties;
};

static struct _category_t _OBJC_$_CATEGORY_TBPerson_$_Learn __attribute__ ((used, section ("__DATA,__objc_const"))) = 
{
	"TBPerson",
	0, // &OBJC_CLASS_$_TBPerson,
	(const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_TBPerson_$_Learn,
	(const struct _method_list_t *)&_OBJC_$_CATEGORY_CLASS_METHODS_TBPerson_$_Learn,
	(const struct _protocol_list_t *)&_OBJC_CATEGORY_PROTOCOLS_$_TBPerson_$_Learn,
	(const struct _prop_list_t *)&_OBJC_$_PROP_LIST_TBPerson_$_Learn,
};

/*
	接下来我们分别查看
	_OBJC_$_CATEGORY_INSTANCE_METHODS_TBPerson_$_Learn(对象方法)
	_OBJC_$_CATEGORY_CLASS_METHODS_TBPerson_$_Learn(类方法)
	_OBJC_CATEGORY_PROTOCOLS_$_TBPerson_$_Learn(协议信息)
	_OBJC_$_PROP_LIST_TBPerson_$_Learn(属性信息)
*/

//	可以看到对象方法testOne
static struct /*_method_list_t*/ {
	unsigned int entsize;  // sizeof(struct _objc_method)
	unsigned int method_count;
	struct _objc_method method_list[1];
} _OBJC_$_CATEGORY_INSTANCE_METHODS_TBPerson_$_Learn __attribute__ ((used, section ("__DATA,__objc_const"))) = {
	sizeof(_objc_method),
	1,
	{{(struct objc_selector *)"testOne", "v16@0:8", (void *)_I_TBPerson_Learn_testOne}}
};
//	可以看到类方法testTwo
static struct /*_method_list_t*/ {
	unsigned int entsize;  // sizeof(struct _objc_method)
	unsigned int method_count;
	struct _objc_method method_list[1];
} _OBJC_$_CATEGORY_CLASS_METHODS_TBPerson_$_Learn __attribute__ ((used, section ("__DATA,__objc_const"))) = {
	sizeof(_objc_method),
	1,
	{{(struct objc_selector *)"testTwo", "v16@0:8", (void *)_C_TBPerson_Learn_testTwo}}
};
//	可以看到Category中遵循的协议
static struct /*_protocol_list_t*/ {
	long protocol_count;  // Note, this is 32/64 bit
	struct _protocol_t *super_protocols[2];
} _OBJC_CATEGORY_PROTOCOLS_$_TBPerson_$_Learn __attribute__ ((used, section ("__DATA,__objc_const"))) = {
	2,
	&_OBJC_PROTOCOL_NSCopying,
	&_OBJC_PROTOCOL_NSCoding
};
//	可以看到Category中增加的一个属性的信息
static struct /*_prop_list_t*/ {
	unsigned int entsize;  // sizeof(struct _prop_t)
	unsigned int count_of_properties;
	struct _prop_t prop_list[1];
} _OBJC_$_PROP_LIST_TBPerson_$_Learn __attribute__ ((used, section ("__DATA,__objc_const"))) = {
	sizeof(_prop_t),
	1,
	{{"name","T@\"NSString\",&,N"}}
};


============================== 源码解读顺序 ==============================
/*
	1、objc-os.mm(运行时的入口)
		1.1、_objc_init(运行时的初始化)
		1.2、map_images
		1.3、map_images_nolock
	2、objc-runtime-new.mm
		2.1、_read_images(读取模块、镜像)
		2.2、remethodizeClass(重新方法化)
		2.3、attachCategories(将分类附加到类中)
		2.4、attachLists
		2.5、realloc、memmove、memcpy
*/

void _objc_init(void)
{
    static bool initialized = false;
    if (initialized) return;
    initialized = true;
    
    // fixme defer initialization until an objc-using image is found?
    environ_init();
    tls_init();
    static_init();
    runtime_init();
    exception_init();
    cache_init();
    _imp_implementationWithBlock_init();

    _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[])
{
    mutex_locker_t lock(runtimeLock);
    return map_images_nolock(count, paths, mhdrs);
}
============================== 核心方法查看 ==============================
/*
	rw->methods.attachLists(mlists, mcount);
	//  将分类中的方法数组放到大数组中(由数组的下标可以看到先参与编译的分类反而放到数组的后面)
	mlists[ATTACH_BUFSIZ - ++mcount] = mlist;
	根据传入的参数 Class cls 来判断是类对象还是元类对象
	1、将所有分类的对象方法附加到类对象的方法列表中
	2、类方法附加到元类对象的方法列表中
	rw->methods.attachLists(mlists + ATTACH_BUFSIZ - mcount, mcount);
	
	rw->properties.attachLists(proplists, propcount);
	//  将分类中的属性数组放到大数组中(由数组的下标可以看到先参与编译的分类反而放到数组的后面)
	proplists[ATTACH_BUFSIZ - ++propcount] = proplist;
	//  将所有分类中的属性附加到类的属性列表中
	rw->properties.attachLists(proplists + ATTACH_BUFSIZ - propcount, propcount);
	
	rw->protocols.attachLists(protolists, protocount);
	//  将分类中的协议数组放到大数组中(由数组的下标可以看到先参与编译的分类反而放到数组的后面)
	protolists[ATTACH_BUFSIZ - ++protocount] = protolist;
	//  将所有分类中的协议附加到类的协议列表中
	rw->protocols.attachLists(protolists + ATTACH_BUFSIZ - protocount, protocount);
*/
static void
attachCategories(Class cls, const locstamped_category_t *cats_list, uint32_t cats_count,
                 int flags)
{
    if (slowpath(PrintReplacedMethods)) {
        printReplacements(cls, cats_list, cats_count);
    }
    if (slowpath(PrintConnecting)) {
        _objc_inform("CLASS: attaching %d categories to%s class '%s'%s",
                     cats_count, (flags & ATTACH_EXISTING) ? " existing" : "",
                     cls->nameForLogging(), (flags & ATTACH_METACLASS) ? " (meta)" : "");
    }
    constexpr uint32_t ATTACH_BUFSIZ = 64;
    method_list_t   *mlists[ATTACH_BUFSIZ];
    property_list_t *proplists[ATTACH_BUFSIZ];
    protocol_list_t *protolists[ATTACH_BUFSIZ];

    uint32_t mcount = 0;
    uint32_t propcount = 0;
    uint32_t protocount = 0;
    bool fromBundle = NO;
    bool isMeta = (flags & ATTACH_METACLASS);
    auto rw = cls->data();

    for (uint32_t i = 0; i < cats_count; i++) {
        auto& entry = cats_list[i];

        method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
        if (mlist) {
            if (mcount == ATTACH_BUFSIZ) {
                prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
                rw->methods.attachLists(mlists, mcount);
                mcount = 0;
            }
            mlists[ATTACH_BUFSIZ - ++mcount] = mlist;
            fromBundle |= entry.hi->isBundle();
        }

        property_list_t *proplist =
            entry.cat->propertiesForMeta(isMeta, entry.hi);
        if (proplist) {
            if (propcount == ATTACH_BUFSIZ) {
                rw->properties.attachLists(proplists, propcount);
                propcount = 0;
            }
            proplists[ATTACH_BUFSIZ - ++propcount] = proplist;
        }

        protocol_list_t *protolist = entry.cat->protocolsForMeta(isMeta);
        if (protolist) {
            if (protocount == ATTACH_BUFSIZ) {
                rw->protocols.attachLists(protolists, protocount);
                protocount = 0;
            }
            protolists[ATTACH_BUFSIZ - ++protocount] = protolist;
        }
    }

    if (mcount > 0) {
        prepareMethodLists(cls, mlists + ATTACH_BUFSIZ - mcount, mcount, NO, fromBundle);
        rw->methods.attachLists(mlists + ATTACH_BUFSIZ - mcount, mcount);
        if (flags & ATTACH_EXISTING) flushCaches(cls);
    }
    
    rw->properties.attachLists(proplists + ATTACH_BUFSIZ - propcount, propcount);
    rw->protocols.attachLists(protolists + ATTACH_BUFSIZ - protocount, protocount);
}

/*
	memmove(array()->lists + addedCount, 
			array()->lists,
			oldCount * sizeof(array()->lists[0]));
	表示将 array()->lists  挪动到 array()->lists + addedCount 的位置,挪动的长度为 oldCount * sizeof(array()->lists[0])
	
	memcpy(array()->lists, 
		  addedLists,
		  addedCount * sizeof(array()->lists[0]));
	表示将 addedLists 拷贝到 array()->lists 位置,拷贝的长度为 addedCount * sizeof(array()->lists[0]) 
	
	memmove与memcpy
		它们的作用是一样的,唯一的区别是,当内存发生局部重叠的时候,memmove保证拷贝的结果是正确的,memcpy不保证拷贝的结果的正确性。
	所以,如果分类和类中有同样的方法时,调用的是分类的方法,而不是类的方法 (消息发送记住的方法查找顺序)
*/
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;
        //	realloc 扩容
        setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
        array()->count = newCount;
        
        memmove(array()->lists + addedCount, array()->lists,
                oldCount * sizeof(array()->lists[0]));
                
        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]));
    }
}
PS 此文为学习 李明杰 老师的 iOS底层原理课程所写笔记
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值