上篇 类的加载 中我们了解了 dyld 在编译阶段通过 _dyld_objc_notify_register 方法中的 map_images 对类进行加载的流程,这篇我们再来看一下如果在有分类时,分类加载的原理
1、Category(分类)
我们准备一段创建了分类的代码,为分类 尝试添加属性和方法
1.1、特性
- 专门用来给类添加新的方法
- 不能给类添加成员属性,添加后无法读取(可通过
Runtime给分类添加属性) - 分类中使用
@property定义变量,只会生成变量的getter/setter方法的声明,不能生成方法的实现和下划线开头的成员变量
1.1、转译C++分析
- 通过终端使用
clang -rewrite-objc main.m将文件转译为.cpp文件 - 打开cpp文件我们可以找到
_category_t结构体,这里存放的就是 编译阶段(运行后会与本类放到一起) 产生的分类文件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; };- 实例方法列表与类方法列表都放在这个 _category_t 结构中,不存在分元类这么个东西
- _category_t 结构中 没有 ivars 的项,所以这也是为什么
分类不能添加成员变量的原因
1.2、类扩展(匿名分类)
- 可以给类添加成员变量、属性,但是为私有变量
- 可以给类添加方法,也是私有方法
2、关联对象
1.1、为分类动态添加属性
虽然分类不能添加成员变量,但我们可以通过objc_setAssociatedObject(objcet, key, value, 关联策略)与objc_getAssociatedObject(object, key)方法来给分类设置 属性
void
objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
{
_object_set_associative_reference(object, key, value, policy);
}
id
objc_getAssociatedObject(id object, const void *key)
{
return _object_get_associative_reference(object, key);
}
- objc_setAssociatedObject 与 objc_getAssociatedObject 为中间层代码,目的是对业务层进行隔离,因为底层
API根据不同版本会发生变化,但中间层代码是保持不变的
1.2、objc_setAssociatedObject
_object_set_associative_reference
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封装为 DisguisedPtr
DisguisedPtr<objc_object> disguised{(objc_object *)object};
//将关联策略与value封装为 ObjcAssociation
ObjcAssociation association{policy, value};
// retain the new value (if any) outside the lock.
//对关联策略进行处理
association.acquireValue();
bool isFirstAssociation = false;
{
//加锁,保证同一时间只有一个线程操作 AssociationsHashMap
AssociationsManager manager;
//拿到全局唯一的存放Associations的哈希表
AssociationsHashMap &associations(manager.get());
if (value) {
//返回以 disguised(封装的object)为key的迭代器
//若查到 disguised 则返回迭代器,若没有则以 disguised 为key,以 ObjectAssociationMap(封装的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();
}
-
AssociationsManager
class AssociationsManager { using Storage = ExplicitInitDenseMap<DisguisedPtr<objc_object>, ObjectAssociationMap>; //静态的哈希表,因此全局唯一 static Storage _mapStorage; public: //C++的构造与析构函数,在构造时加锁,析构时开锁 AssociationsManager() { AssociationsManagerLock.lock(); } ~AssociationsManager() { AssociationsManagerLock.unlock(); } AssociationsHashMap &get() { //返回上边静态的哈希表,因此AssociationsHashMap也是全局唯一的 return _mapStorage.get(); } static void init() { _mapStorage.init(); } };className() { 类或结构体构造时执行自定义方法 }
~className() { 类或结构体析构时执行自定义方法 }- 构造时加锁,析构时开锁,保证操作哈希表时线程安全
-
AssociationsHashMap
//DenseMap就是张哈希表 typedef DenseMap<const void *, ObjcAssociation> ObjectAssociationMap; typedef DenseMap<DisguisedPtr<objc_object>, ObjectAssociationMap> AssociationsHashMap;- AssociationsHashMap 中存的是:
DisguisedPtr:对object的封装ObjectAssociationMap:将 封装了关联策略与值的ObjcAssociation,再封装成的哈希表
- AssociationsHashMap 中存的是:
-
try_emplace
// Inserts key,value pair into the map if the key isn't already in the map. // The value is constructed in-place if the key is not in the map, otherwise // it is not moved. template <typename... Ts> std::pair<iterator, bool> try_emplace(const KeyT &Key, Ts &&... Args) { //类似cache中的 bucket_t,也是桶子容器 BucketT *TheBucket; //LookupBucketFor 查找Key是否已存在 if (LookupBucketFor(Key, TheBucket)) return std::make_pair( makeIterator(TheBucket, getBucketsEnd(), true), false); // Already in map. // Otherwise, insert the new element. TheBucket = InsertIntoBucket(TheBucket, Key, std::forward<Ts>(Args)...); return std::make_pair( makeIterator(TheBucket, getBucketsEnd(), true), true); }-
以 disguised 为key查找返回迭代器,很好理解就是 先找设置了关联的类,返回这个类中存放茫茫多关联属性的哈希表的迭代器
-
若查到 disguised 则返回迭代器,没查到则先插入到表(以 disguised 为key、 ObjectAssociationMap(封装的value与关联策略)为值),再返回迭代器
-
1.3、objc_getAssociatedObject
_object_get_associative_reference
id
_object_get_associative_reference(id object, const void *key)
{
ObjcAssociation association{};
{
//与 objc_setAssociatedObject 中类似的根据key找迭代器
AssociationsManager manager;
AssociationsHashMap &associations(manager.get());
AssociationsHashMap::iterator i = associations.find((objc_object *)object);
if (i != associations.end()) {
ObjectAssociationMap &refs = i->second;
ObjectAssociationMap::iterator j = refs.find(key);
if (j != refs.end()) {
association = j->second;
association.retainReturnedValue();
}
}
}
//添加到自动释放池
return association.autoreleaseReturnedValue();
}
小结
-
唯一表 AssociationsHashMap 中有茫茫多 disguised 为key的哈希表
-
每一个 disguised 是一个 设置了关联内容的类,里边有很多个 ObjectAssociationMap
-
每一个 ObjectAssociationMap 中存放的是 自定义的key名 与 ObjcAssociation(关联策略与value的封装)
3、Category加载
rwe:在 类通过runtime的api动态修改了方法、属性、协议 或者 类有分类并且都为非懒加载 的情况时才存在;里边以二维数组的形式存放数据- 思路:锁定 rwe 即可锁定分类的加载时机
类为非懒加载,分类为非懒加载:ro中只有类的数据,没有分类的数据;分类数据在rwe
类为懒加载,分类为非懒加载:类与分类中数据在ro中了,没有rwe
类为非懒加载,分类为懒加载:类与分类中数据在ro中了,没有rwe
类为懒加载,分类为懒加载:类与分类中数据在ro中了,没有rwe
3.1、从 rwe 入手探索分类加载
- 之前我们探索类的时候按着 objc_class --> bits --> class_rw_t 路线曾经看过rwe中的结构
- 在上边结构里我们可以根据方法名看到rwe的创建方法
extAllocIfNeeded,那么接下来我们就可以寻找一下都在什么地方有调用这个方法
- 在上边结构里我们可以根据方法名看到rwe的创建方法
3.2、attachCategories (attach:附加)
- 在 attachCategories 方法中创建rwe
3.3、attachToClass / load_categories_nolock
- 全局搜索,这两个方法会调用 attachCategories 去创建rwe
//attachToClass
void attachToClass(Class cls, Class previously, int flags)
{
runtimeLock.assertLocked();
ASSERT((flags & ATTACH_CLASS) ||
(flags & ATTACH_METACLASS) ||
(flags & ATTACH_CLASS_AND_METACLASS));
auto &map = get();
auto it = map.find(previously);
if (it != map.end()) {
category_list &list = it->second;
if (flags & ATTACH_CLASS_AND_METACLASS) {
int otherFlags = flags & ~ATTACH_CLASS_AND_METACLASS;
// attachCategories
attachCategories(cls, list.array(), list.count(), otherFlags | ATTACH_CLASS);
attachCategories(cls->ISA(), list.array(), list.count(), otherFlags | ATTACH_METACLASS);
} else {
attachCategories(cls, list.array(), list.count(), flags);
}
map.erase(it);
}
}
//load_categories_nolock
static void load_categories_nolock(header_info *hi) {
bool hasClassProperties = hi->info()->hasCategoryClassProperties();
size_t count;
auto processCatlist = [&](category_t * const *catlist) {
for (unsigned i = 0; i < count; i++) {
category_t *cat = catlist[i];
Class cls = remapClass(cat->cls);
locstamped_category_t lc{cat, hi};
if (!cls) {
// Category's target class is missing (probably weak-linked).
// Ignore the category.
if (PrintConnecting) {
_objc_inform("CLASS: IGNORING category \?\?\?(%s) %p with "
"missing weak-linked target class",
cat->name, cat);
}
continue;
}
// Process this category.
if (cls->isStubClass()) {
// Stub classes are never realized. Stub classes
// don't know their metaclass until they're
// initialized, so we have to add categories with
// class methods or properties to the stub itself.
// methodizeClass() will find them and add them to
// the metaclass as appropriate.
if (cat->instanceMethods ||
cat->protocols ||
cat->instanceProperties ||
cat->classMethods ||
cat->protocols ||
(hasClassProperties && cat->_classProperties))
{
objc::unattachedCategories.addForClass(lc, cls);
}
} else {
// First, register the category with its target class.
// Then, rebuild the class's method lists (etc) if
// the class is realized.
if (cat->instanceMethods || cat->protocols
|| cat->instanceProperties)
{
if (cls->isRealized()) {
//此处调用 attachCategories
attachCategories(cls, &lc, 1, ATTACH_EXISTING);
} else {
objc::unattachedCategories.addForClass(lc, cls);
}
}
if (cat->classMethods || cat->protocols
|| (hasClassProperties && cat->_classProperties))
{
if (cls->ISA()->isRealized()) {
//此处调用 attachCategories
attachCategories(cls->ISA(), &lc, 1, ATTACH_EXISTING | ATTACH_METACLASS);
} else {
objc::unattachedCategories.addForClass(lc, cls->ISA());
}
}
}
}
};
processCatlist(hi->catlist(&count));
processCatlist(hi->catlist2(&count));
}
3.4、attachLists
整合分类添加的方法到本类中
- 上篇提到了使用
realizeClassWithoutSwift中的methodizeClass将分类方法添加到本类中也是通过这个方法实现的,也串联起来了