iOS分类底层实现原理小记

http://www.jianshu.com/p/b7169a5a558e

OS 分类底层是怎么实现的?
本文将分如下四个模块进行探究

  1. 分类的结构体
  2. 编译时的分类
  3. 分类的加载
  4. 总结

本文使用的runtime源码版本是 objc4 - 680
文中类与分类代码如下

//类
@interface Person : NSObject
@property (nonatomic ,copy) NSString *presonName;
@end @implementation Person - (void)doSomeThing{ NSLog(@"Person"); } @end
// 分类
@interface Person(categoryPerson)
@property (nonatomic ,copy) NSString *categoryPersonName;
@end

@implementation Person(categoryPerson) - (void)doSomeThing{ NSLog(@"categoryPerson"); } @end

1.分类的结构体

struct _category_t {
    const char *name;//类名 struct _class_t *cls;//类 const struct _method_list_t *instance_methods;//category中所有给类添加的实例方法的列表(instanceMethods) const struct _method_list_t *class_methods;//category中所有添加的类方法的列表(classMethods) const struct _protocol_list_t *protocols;//category实现的所有协议的列表(protocols) const struct _prop_list_t *properties;//category中添加的所有属性(instanceProperties) }; 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; // 属性列表 // 如果是元类,就返回类方法列表;否则返回实例方法列表 method_list_t *methodsForMeta(bool isMeta) { if (isMeta) { return classMethods; } else { return instanceMethods; } } // 如果是元类,就返回 nil,因为元类没有属性;否则返回实例属性列表,但是...实例属性 property_list_t *propertiesForMeta(bool isMeta) { if (isMeta) { return nil; // classProperties; } else { return instanceProperties; } } };

2.编译时的分类

2.1分类的属性

// Person(categoryPerson) 属性列表
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_Person_$_categoryPerson __attribute__ ((used, section ("__DATA,__objc_const"))) = { sizeof(_prop_t), 1, {{"categoryPersonName","T@\"NSString\",C,N"}} }; // Person 属性列表 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_Person __attribute__ ((used, section ("__DATA,__objc_const"))) = { sizeof(_prop_t), 1, {{"presonName","T@\"NSString\",C,N,V_presonName"}} };

对比上述代码可以发现:在分类中可以声明属性,并且同样会生成一个 _prop_list_t 的结构体

2.2分类的实例变量?

// Person 实例变量列表

static struct /*_ivar_list_t*/ { unsigned int entsize; // sizeof(struct _prop_t) unsigned int count; struct _ivar_t ivar_list[1]; } _OBJC_$_INSTANCE_VARIABLES_Person __attribute__ ((used, section ("__DATA,__objc_const"))) = { sizeof(_ivar_t), 1, {{(unsigned long int *)&OBJC_IVAR_$_Person$_presonName, "_presonName", "@\"NSString\"", 3, 8}} };

因为 _category_t 这个结构体中并没有 _ivar_list_t
所以在编译时系统没有Person(categoryPerson) 没有生成类似的相应结构体,也没有生成 _categoryPersonName

2.3分类的实例方法

// Person 实例方法结构体
static struct /*_method_list_t*/ { unsigned int entsize; // sizeof(struct _objc_method) unsigned int method_count; struct _objc_method method_list[3]; } _OBJC_$_INSTANCE_METHODS_Person __attribute__ ((used, section ("__DATA,__objc_const"))) = { sizeof(_objc_method), 3, {{(struct objc_selector *)"doSomeThing", "v16@0:8", (void *)_I_Person_doSomeThing}, {(struct objc_selector *)"presonName", "@16@0:8", (void *)_I_Person_presonName}, {(struct objc_selector *)"setPresonName:", "v24@0:8@16", (void *)_I_Person_setPresonName_}} }; // Person(categoryPerson )实例方法结构体 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_Person_$_categoryPerson __attribute__ ((used, section ("__DATA,__objc_const"))) = { sizeof(_objc_method), 1, {{(struct objc_selector *)"doSomeThing", "v16@0:8", (void *)_I_Person_categoryPerson_doSomeThing}} };

对比上述方法可以看到:虽然分类可以声明属性,但是编译时,系统并没有生成分类属性的 get/set 方法,所以,这就是为什么分类要利用
runtime 动态添加属性,如何动态添加属性,有兴趣的同学可以查看下面文章 iOS分类中通过runtime添加动态属性

2.4分类的结构体

// Person(categoryPerson ) 结构体
static struct _category_t _OBJC_$_CATEGORY_Person_$_categoryPerson __attribute__ ((used, section ("__DATA,__objc_const"))) = { "Person", 0, // &OBJC_CLASS_$_Person, (const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_Person_$_categoryPerson, 0, 0, (const struct _prop_list_t *)&_OBJC_$_PROP_LIST_Person_$_categoryPerson, };

这是系统在编译时实例化 _category_t 生成的
_OBJC_$_CATEGORY_Person_$_categoryPerson

2.5分类数组

static struct _category_t *L_OBJC_LABEL_CATEGORY_$ [1] __attribute__((used, section ("__DATA, __objc_catlist,regular,no_dead_strip")))= {
    &_OBJC_$_CATEGORY_Person_$_categoryPerson, };

编译器最后生成了一个数组,数组的元素就是我们创建的各个分类,用来在运行时加载分类。

3.分类的加载

3.1加载分类调用栈

_objc_init
└──map_2_images
    └──map_images_nolock
        └──_read_images

分类加载的调用栈如上述

  • _objc_init 算是整个 objc4 的入口,进行了一些初始化操作,注册了镜像状态改变时的回调函数
  • map_2_images 主要是加锁并调用 map_images_nolock
  • map_images_nolock 在这个函数中,完成所有 class 的注册、fixup等工作,还有初始化自动释放池、初始化 side table 等工作并在函数后端调用了 _read_images
  • _read_images 方法干了很多苦力活,比如加载类、Protocol、Category,加载分类的代码就写在 _read_images 函数的尾部

该调用栈入口函数 void _objc_init(void) 在 objc-os.mm 中,有兴趣的同学可以去看看这些函数里都做了什么

3.2 _read_images 中加载分类的源码

加载分类的源码主要做了两件事

  • 把category的实例方法、协议以及属性添加到类上
  • 把category的类方法和协议添加到类的metaclass上

略去与本文无关的代码,得到如下代码

// 获取 镜像中的所有分类
category_t **catlist = _getObjc2CategoryList(hi, &count);
// 遍历 catlist
for (i = 0; i < count; i++) {
    category_t *cat = catlist[i];
    Class cls = remapClass(cat->cls); if (cat->instanceMethods || cat->protocols || cat->instanceProperties) { addUnattachedCategoryForClass(cat, cls, hi); if (cls->isRealized()) { remethodizeClass(cls); classExists = YES; } } if (cat->classMethods || cat->protocols /* || cat->classProperties */) { addUnattachedCategoryForClass(cat, cls->ISA(), hi); if (cls->ISA()->isRealized()) { remethodizeClass(cls->ISA()); } } }

做上述事情主要用到是如下两个函数

  • addUnattachedCategoryForClass(cat, cls, hi) 为类添加未依附的分类
    执行过程伪代码:
    1.取得存储所有 unattached 分类的列表

    NXMapTable *cats = unattachedCategories();

    2.从 cats 列表中找倒 cls 对应的 unattached 分类的列表

    category_list *list = (category_list *)NXMapGet(cats, cls);

    3.将新来的分类 cat 添加刚刚开辟的位置上

    list->list[list->count++] = (locstamped_category_t){cat, catHeader};

    4.将新的 list 重新插入 cats 中,会覆盖老的 list

    NXMapInsert(cats, cls, list);

    执行完上述过程后,系统将这个分类放到了一个 cls 对应的 unattached 分类的 list 中(有点绕口....),这个 list 会在 remethodizeClass(cls) 方法用到

  • remethodizeClass(cls)
    执行过程伪代码:
    1.取得 cls 类的 unattached 的分类列表

    category_list *cats = unattachedCategoriesForClass(cls, false/*not realizing*/)

    2.将 unattached 的分类列表 attach 到 cls 类上

    attachCategories(cls, cats, true /* 清空方法缓存 flush caches*/);

    执行完上述过程后,系统就把category的实例方法、协议以及属性添加到类上

  • 最后再来看一下
    attachCategories(cls, cats, true /* 清空方法缓存 flush caches*/)内部的实现过程
    1.在堆上创建方法、属性、协议数组,用来存储分类的方法、属性、协议

    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));

    2.遍历 cats ,取出各个分类的方法、属性、协议,并填充到上述代码创建的数组中

    int mcount = 0; // 记录方法的数量
    int propcount = 0; // 记录属性的数量
    int protocount = 0; // 记录协议的数量 int i = cats->count; // 从后开始,保证先取最新的分类 bool fromBundle = NO; // 记录是否是从 bundle 中取的 while (i--) { // 从后往前遍历 auto& entry = cats->list[i]; // 分类,locstamped_category_t 类型 // 取出分类中的方法列表;如果是元类,取得的是类方法列表;否则取得的是实例方法列表 method_list_t *mlist = entry.cat->methodsForMeta(isMeta); if (mlist) { mlists[mcount++] = mlist; // 将方法列表放入 mlists 方法列表数组中 fromBundle |= entry.hi->isBundle(); // 分类的头部信息中存储了是否是 bundle,将其记住 } // 取出分类中的属性列表,如果是元类,取得是nil property_list_t *proplist = entry.cat->propertiesForMeta(isMeta); if (proplist) { proplists[propcount++] = proplist; // 将属性列表放入 proplists 属性列表数组中 } // 取出分类中遵循的协议列表 protocol_list_t *protolist = entry.cat->protocols; if (protolist) { protolists[protocount++] = protolist; // 将协议列表放入 protolists 协议列表数组中 } }

    3.取出 cls 的 class_rw_t 数据

    auto rw = cls->data();

    4.存储方法、属性、协议数组到 rw

    // 准备 mlists 中的方法
    prepareMethodLists(cls, mlists, mcount/*方法列表的数量*/, NO/*不是基本方法*/, fromBundle/*是否来自bundle*/); // 将新属性列表添加到 rw 中的属性列表数组中 rw->properties.attachLists(proplists, propcount); // 释放 proplists free(proplists); // 释放 proplists // 将新协议列表添加到 rw 中的协议列表数组中 rw->protocols.attachLists(protolists, protocount); // 释放 protolists free(protolists); // 释放 protolists // 将新协议列表添加到 rw 中的协议列表数组中 rw->protocols.attachLists(protolists, protocount); // 释放 protolists free(protolists);

4.总结

至此,本文接近尾声
希望本文的读者能够了解到

  • 分类的结构体
  • 分类中是否能添加属性
  • 分类中是否有实例变量
  • 分类是如何 attach 到类上的

行笔简陋,如有问题,敬请指正

转载于:https://www.cnblogs.com/feng9exe/p/7133767.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
iOS中,可以使用互斥锁来实现线程间的互斥访问,保护共享资源的安全。互斥锁的实现原理可以分为两个层面:内核层面和用户层面。 1. 内核层面的互斥锁实现原理: - POSIX互斥锁:在iOS中,使用POSIX标准的互斥锁pthread_mutex_t来实现。它是基于内核提供的原语实现的,通过系统调用来管理锁的状态。当一个线程请求锁时,如果锁已经被占用,则该线程会被阻塞,并进入等待状态。当持有锁的线程释放锁时,等待队列中的一个线程会被唤醒,获取到锁继续执行。 2. 用户层面的互斥锁实现原理: - 自旋锁:自旋锁是一种忙等待的锁机制,它通过循环检查锁的状态,直到获取到锁为止。在iOS中,可以使用OSSpinLock来实现自旋锁。当一个线程请求锁时,如果锁已经被占用,则该线程会一直循环检查锁的状态,直到获取到锁后才继续执行。自旋锁适用于临界区代码执行时间短暂,且争用锁的线程数较少的情况。 - 互斥锁(NSLock、NSRecursiveLock、NSConditionLock):在iOS中,还提供了一些高级的互斥锁类,如NSLock、NSRecursiveLock、NSConditionLock。这些锁类是基于底层的pthread_mutex_t实现的,提供了更方便的API和更高级的功能。NSLock和NSRecursiveLock是互斥锁,可以保护临界区代码的互斥访问。NSRecursiveLock允许同一个线程对锁进行多次加锁,避免死锁。NSConditionLock是一种条件锁,可以在特定条件满足时才允许访问临界区代码。 需要注意的是,使用互斥锁时,应遵循良好的加锁和解锁的原则,避免死锁和资源泄漏等问题。同时,在高并发的场景中,也可以考虑使用其他更高级的同步机制,如信号量(dispatch_semaphore)或读写锁(pthread_rwlock_t),以满足不同的需求。 希望以上解答对你有所帮助!如果还有其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值