前言
- 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 。 源码如下。
void
_object_set_associative_reference(id object, const void *key, id value, uintptr_t policy)
{
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));
DisguisedPtr<objc_object> disguised{(objc_object *)object};
ObjcAssociation association{policy, value};
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) {
isFirstAssociation = true;
}
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);
}
}
}
}
}
if (isFirstAssociation)
object->setHasAssociatedObjects();
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
|