iOS开发底层之类加载下 (关联对象) - 14
Posted iOS_developer_zhong
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS开发底层之类加载下 (关联对象) - 14相关的知识,希望对你有一定的参考价值。
文章目录
前言
- ro rw rwe的补充?
ro (干净内存,只读) -》 rw (脏内存,昂贵,所以需要优化) -》 rwe 。
之所以会有rw,是因为运行时功能,会修改内存,这时候因为Ro只读,不能修改,所以产生了rw, 用来去追踪, 如果在运行时操作了分类,动态添加方法, 会浪费很多内存,但有的类是不会通过运行时去进行分类,去动态添加方法,所以有了 rwe , 里面存放运行时添加的分类,属性,协议等。 对rw的一个extension。 - 本节主要讲的内容为分类的扩展
一、分类什么时候加载?
- 根据attachCategories方法,去反推,哪些地方调用了这个方法。
最终推导出来只有attachToClass 方法调用。
二、attachCategories
项目新建好分类,并写上load方法, 在所有的这个方法处,打上断点,观察执行顺序。 多种情况说明
分类 + 类搭配加载
-
两个都有load方法: _read_images 懒加载类 -》 realizeClassWithoutSwift -》 load_categories_nolock -》 attachCategories
-
分类没有load方法, 主类有load方法
_read_images -》 realizeClassWithoutSwift -》 methodizeClass -》 attachToClass ==没有走attachCategories 方法。 == -
分类有load 方法, 主类没有load方法
_read_images -》 realizeClassWithoutSwift -》 methodizeClass -》 attachToClass ==没有走attachCategories 方法。 == -
两个都没有load方法
直接到main函数了,说明方法都没有执行,走了懒加载流程了。
总结:4种情况:
- 非懒加载类 + 非懒加载类
_getObjc2NonlazyClassList -》 readClass - 》 realizeClassWithoutSwift -》 methodizeClass -》 load_categories_nolock --》 attachToClass -》 attachCategories - 懒加载类 + 非懒加载分类: 会迫使类成为非懒加载类提前加载数据。
- 懒加载类 + 懒加载分类: 消息第一次调用,才会加载数据。
- 非懒加载类 + 懒加载分类: read_image就开始加载数据。
三、 类扩展、分类 应用层
1. category:分类
- 专门用来给类添加新的方法。
- 不能给类添加成员属性, 添加了成员变量,也无法取到。
- 但我们可以通过runtime去给分类添加属性。
- 分类中用@perperty定义变量, 只会生成getter,setter的方法申明, 不能生成方法实现和带下划线的成员变量。
2. extension:扩展
- 特殊的分类,匿名分类。
- 可以添加成员属性,是私有变量
- 可以给类添加方法, 是私有方法
3 .关联对象 (AssociatedObject)
- 方法为 _object_set_associative_reference 。 源码如下。
/**
关联对象 : 存储 object - cate_name -> value - policy
*/
void
_object_set_associative_reference(id object, const void *key, id value, uintptr_t policy)
{
// This code used to work when nil was passed for object and key. Some code
// probably relies on that to not crash. Check and handle it explicitly.
// rdar://problem/44094390
if (!object && !value) return;
if (object->getIsa()->forbidsAssociatedObjects())
_objc_fatal("objc_setAssociatedObject called on instance (%p) of class %s which does not allow associated objects", object, object_getClassName(object));
//这是将object强转成objc_object类型,得到DisguisedPtr<objc_object>类型的对象disguised
// 或者是这样: DisguisedPtr<objc_object> disguised = (objc_object *)object
DisguisedPtr<objc_object> disguised{(objc_object *)object};
ObjcAssociation association{policy, value};
// retain the new value (if any) outside the lock.
association.acquireValue();
bool isFirstAssociation = false;
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.get());
if (value) {
auto refs_result = associations.try_emplace(disguised, ObjectAssociationMap{});
if (refs_result.second) {
/* it's the first association we make */
isFirstAssociation = true;
}
/* establish or replace the association */
auto &refs = refs_result.first->second;
auto result = refs.try_emplace(key, std::move(association));
if (!result.second) {
association.swap(result.first->second);
}
} else {
auto refs_it = associations.find(disguised);
if (refs_it != associations.end()) {
auto &refs = refs_it->second;
auto it = refs.find(key);
if (it != refs.end()) {
association.swap(it->second);
refs.erase(it);
if (refs.size() == 0) {
associations.erase(refs_it);
}
}
}
}
}
// Call setHasAssociatedObjects outside the lock, since this
// will call the object's _noteAssociatedObjects method if it
// has one, and this may trigger +initialize which might do
// arbitrary stuff, including setting more associated objects.
if (isFirstAssociation)
object->setHasAssociatedObjects();
// release the old value (outside of the lock).
association.releaseHeldValue();
}
源码解读: objc_setAssociatedObject需要传入4个参数,分别是被关联者,关联标记,关联对象的值和关联策略。
核心流程为: 代码中先创建了AssociationsManager对象manager,再获取全局的hashMap表,然后根据value进行处理
4. AssociationsHashMap
- 源码如下:
class AssociationsManager {
using Storage = ExplicitInitDenseMap<DisguisedPtr<objc_object>, ObjectAssociationMap>;
static Storage _mapStorage;
public:
AssociationsManager() { AssociationsManagerLock.lock(); }
~AssociationsManager() { AssociationsManagerLock.unlock(); }
AssociationsHashMap &get() {
return _mapStorage.get();
}
static void init() {
_mapStorage.init();
}
};
源码解读: AssociationsHashMap是通过manager.get()获取HashMap,它的源码是通过_mapStorage调用get()方法实现的,而_mapStorage是一个static类型,所以获取的这张表是唯一的,这个获取表的过程是个单例方法
总结
1. 设值过程
1.创建一个AssociationsManager管理类;
2.得到唯一的全局静态哈希Map;
3.判断要插入的关联值是否存在:
3.1 存在,则执行4;
3.2 不存在,则进行关联对象插入空流程
4.创建一个空的ObjectAssociationMap去取查询的键值对;
5.如果发现没有这个key,就插入一个空的BucketT,并返回;
6.标记对象存在关联对象;
7.用当前 修饰策略 和 值 组成一个ObjcAssociation替换原来BucketT中的空
8.标记一下ObjectAssociationMap的第一次为false;
2. 取值过程
- HashMap
- 然后根据object获取ObjectAssociationMap bucket
- 再获取ObjectAssociationMap
- 然后获取ObjcAssociation bucket
- 再获取ObjcAssociation
- 最后根据policy返回value
以上是关于iOS开发底层之类加载下 (关联对象) - 14的主要内容,如果未能解决你的问题,请参考以下文章