iOS---12-load_images&类拓展&关联对象原理分析

类扩展 分类

类扩展就是一个匿名的分类.
类扩展在编译的时候就将数据写入到类的信息中,也就ro

分类编译底层是struct category_t,里面存储着分类的对象方法、类方法、属性、协议信息.
在程序运行的时候,runtime会将Category的数据,合并到类信息中(类对象、元类对象中.也就是保存在rw中.

  • 类扩展


    image.png

通过通过_read_images->_getObjc2ClassList->打印ro-> baseMethodList,我们发现:

image.png

此时ro已经有属性生成的set,get方法.
类扩展还可以单独写成一个头文件,记得导入头文件.

image.png
  • 分类底层结构
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;  // 属性列表
};
  1. 从结构体可以知道,有属性列表,所以分类可以声明属性,但是分类只会生成该属性对应的getset的声明,没有去实现该方法。
  2. 结构体没有成员变量列表,所以能声明成员变量。
  • Category的加载处理过程
  1. 通过Runtime加载某个类的所有Category数据
  2. 把所有Category的方法、属性、协议数据,合并到一个大数组中,后面参与编译的Category数据,会在数组的前面
  3. 将合并后的分类数据(方法、属性、协议),插入到类原来数据的前面
    4.既然分类不能声明属性,可以通过关联对象
  • 分类中有cate_name的属性
#import "LGPerson.h"

NS_ASSUME_NONNULL_BEGIN

@interface LGPerson (LG)
@property (nonatomic, copy) NSString *cate_name;
@end

NS_ASSUME_NONNULL_END

为起关联对象


#import "LGPerson+LG.h"
#import 

@implementation LGPerson (LG)


-(void)setCate_name:(NSString *)cate_name{
    /**
    参数一:id object : 给哪个对象添加属性,这里要给自己添加属性,用self。
    参数二:void * == id key : 属性名,根据key获取关联对象的属性的值,在objc_getAssociatedObject中通过次key获得属性的值并返回。
    参数三:id value : 关联的值,也就是set方法传入的值给属性去保存。
    参数四:objc_AssociationPolicy policy : 策略,属性以什么形式保存。
    */
    objc_setAssociatedObject(self, @"name",cate_name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    
    /**
     存储
     manager -> hashMap 有所有的关联对象 - 总表 - 千千万的 -> 关联表 -> index -> 属性
     哈希
     哈希函数 -> index - key (属性) + 地址 (对象)
     */
}


-(NSString *)cate_name{
    /**
    参数一:id object : 获取哪个对象里面的关联的属性。
    参数二:void * == id key : 什么属性,与objc_setAssociatedObject中的key相对应,即通过key值取出value。
    */
    return objc_getAssociatedObject(self, @"name");
}

@end
  • 这些都是平时常用的关联对象代码,那么底层源码为我们做了什么?

  • objc_setAssociatedObject

就是AssociationsManager管理一张总的哈希表,然后里面是一张张小表,通过对象地址找到对象的哈希表,在通过key,去保存.
取值也是,先找到自己的表,在通过key,取出关联的值

//存值
void _object_set_associative_reference(id object, 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;
    
    assert(object);
    
    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));
    
    // retain the new value (if any) outside the lock.
    // 在锁之外保留新值(如果有)。
    ObjcAssociation old_association(0, nil);
    id new_value = value ? acquireValue(value, policy) : nil;
    {
        // 关联对象的管理类
        AssociationsManager manager;
        // 获取关联的 HashMap -> 存储当前关联对象
        AssociationsHashMap &associations(manager.associations());
        // 对当前的对象的地址做按位去反操作 - 就是 HashMap 的key (哈希函数)
        disguised_ptr_t disguised_object = DISGUISE(object);
        if (new_value) {
            // break any existing association.
            // 获取 AssociationsHashMap 的迭代器 - (对象的) 进行遍历
            AssociationsHashMap::iterator i = associations.find(disguised_object);
            if (i != associations.end()) {
                // secondary table exists
                ObjectAssociationMap *refs = i->second;
                // 根据key去获取关联属性的迭代器
                ObjectAssociationMap::iterator j = refs->find(key);
                if (j != refs->end()) {
                    old_association = j->second;
                    // 替换设置新值
                    j->second = ObjcAssociation(policy, new_value);
                } else {
                    // 到最后了 - 直接设置新值
                    (*refs)[key] = ObjcAssociation(policy, new_value);
                }
            } else {
                // create the new association (first time).
                // 如果AssociationsHashMap从没有对象的关联信息表,
                // 那么就创建一个map并通过传入的key把value存进去
                ObjectAssociationMap *refs = new ObjectAssociationMap;
                associations[disguised_object] = refs;
                (*refs)[key] = ObjcAssociation(policy, new_value);
                object->setHasAssociatedObjects();
            }
        } else {
            // setting the association to nil breaks the association.
            // 如果传入的value是nil,并且之前使用相同的key存储过关联对象,
            // 那么就把这个关联的value移除(这也是为什么传入nil对象能够把对象的关联value移除)
            AssociationsHashMap::iterator i = associations.find(disguised_object);
            if (i !=  associations.end()) {
                ObjectAssociationMap *refs = i->second;
                ObjectAssociationMap::iterator j = refs->find(key);
                if (j != refs->end()) {
                    old_association = j->second;
                    refs->erase(j);
                }
            }
        }
    }
    // release the old value (outside of the lock).
    // 最后把之前使用传入的这个key存储的关联的value释放(OBJC_ASSOCIATION_SETTER_RETAIN策略存储的)
    if (old_association.hasValue()) ReleaseValue()(old_association);
}
  • _object_get_associative_reference

id _object_get_associative_reference(id object, void *key) {
    id value = nil;
    uintptr_t policy = OBJC_ASSOCIATION_ASSIGN;
    {
        // 关联对象的管理类
        AssociationsManager manager;
        AssociationsHashMap &associations(manager.associations());
        // 生成伪装地址。处理参数 object 地址
        disguised_ptr_t disguised_object = DISGUISE(object);
        // 所有对象的额迭代器
        AssociationsHashMap::iterator i = associations.find(disguised_object);
        if (i != associations.end()) {
            ObjectAssociationMap *refs = i->second;
            // 内部对象的迭代器
            ObjectAssociationMap::iterator j = refs->find(key);
            if (j != refs->end()) {
                // 找到 - 把值和策略读取出来
                ObjcAssociation &entry = j->second;
                value = entry.value();
                policy = entry.policy();
                // OBJC_ASSOCIATION_GETTER_RETAIN - 就会持有一下
                if (policy & OBJC_ASSOCIATION_GETTER_RETAIN) {
                    objc_retain(value);
                }
            }
        }
    }
    if (value && (policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE)) {
        objc_autorelease(value);
    }
    return value;
}
  • 关联对象的释放在dealloc方法执行时跟着释放了

- (void)dealloc
_objc_rootDealloc(id obj)
objc_object::rootDealloc()
object_dispose(id obj)
void *objc_destructInstance(id obj)
if (assoc) _object_remove_assocations(obj);


void _object_remove_assocations(id object) {
    vector< ObjcAssociation,ObjcAllocator > elements;
    {
        AssociationsManager manager;
        AssociationsHashMap &associations(manager.associations());
        if (associations.size() == 0) return;
        disguised_ptr_t disguised_object = DISGUISE(object);
        AssociationsHashMap::iterator i = associations.find(disguised_object);
        if (i != associations.end()) {
            // copy all of the associations that need to be removed.
            ObjectAssociationMap *refs = i->second;
            for (ObjectAssociationMap::iterator j = refs->begin(), end = refs->end(); j != end; ++j) {
                elements.push_back(j->second);
            }
            // remove the secondary table.
            delete refs;
            associations.erase(i);
        }
    }
    // the calls to releaseValue() happen outside of the lock.
    for_each(elements.begin(), elements.end(), ReleaseValue());
}
  • 总结


    image.png

load_images

load方法在什么时候调用?

查看函数调用栈

image.png

在之前调用load_images

void
load_images(const char *path __unused, const struct mach_header *mh)
{
   // Return without taking locks if there are no +load methods here.
   if (!hasLoadMethods((const headerType *)mh)) return;

   recursive_mutex_locker_t lock(loadMethodLock);

   // Discover load methods
   {
       mutex_locker_t lock2(runtimeLock);
       prepare_load_methods((const headerType *)mh);
   }

   // Call +load methods (without runtimeLock - re-entrant)
   call_load_methods();
}

call_load_methods猜测就是调用load的时候,但是在之前有个局部函数prepare_load_methods,

查看

void prepare_load_methods(const headerType *mhdr)
{
    size_t count, I;

    runtimeLock.assertLocked();

    classref_t const *classlist = 
        _getObjc2NonlazyClassList(mhdr, &count);
    for (i = 0; i < count; i++) {
        schedule_class_load(remapClass(classlist[i]));
    }

    category_t * const *categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
    for (i = 0; i < count; i++) {
        category_t *cat = categorylist[I];
        Class cls = remapClass(cat->cls);
        if (!cls) continue;  // category for ignored weak-linked class
        if (cls->isSwiftStable()) {
            _objc_fatal("Swift class extensions and categories on Swift "
                        "classes are not allowed to have +load methods");
        }
        realizeClassWithoutSwift(cls, nil);
        ASSERT(cls->ISA()->isRealized());
        add_category_to_loadable_list(cat);
    }
}

schedule_class_loadadd_category_to_loadable_list

先取出类的列表,将load方法加入到list,然后在加载分类的load方法加入到list中.

  • call_load_methods
 void *pool = objc_autoreleasePoolPush();

    do {
        // 1. Repeatedly call class +loads until there aren't any more
        while (loadable_classes_used > 0) {
            call_class_loads();
        }

        // 2. Call category +loads ONCE
        more_categories = call_category_loads();

        // 3. Run more +loads if there are classes OR more untried categories
    } while (loadable_classes_used > 0  ||  more_categories);

    objc_autoreleasePoolPop(pool);
    
    

为栈操作,类的先压入栈中,调用为函数指针方式

struct loadable_category {
    Category cat;  // may be nil
    IMP method;
};

load方法,先主后分类
一般方法,分类方法attachlist往前面插
方法调用时找到就返回,分类方法不覆盖
多个分类相同方法,看文件那个加载,就执行那个.

image.png

initalize

  1. initialize在类第一次接收到消息时调用,也就是objc_msgSend()
  2. 先调用父类的+initialize,再调用类的initialize
  • 方法调用都会来到lookUpImpOrForwarz
if (slowpath((behavior & LOOKUP_INITIALIZE) && !cls->isInitialized())) {
        cls = initializeAndLeaveLocked(cls, inst, runtimeLock);

初始化

 initializeNonMetaClass(nonmeta);

调用


  callInitialize(cls);

+initialize的调用过程:

  1. 如果本类和父类都实现了initialize方法,初始化本类时,会先初始化父类(即调用父类的initialize方法),然后再调用本类的initialize方法;
  2. 如果本类没有实现initialize方法,父类实现了initialize方法,则多个子类初始化时会多次调用父类的initialize方法,但是本质上只有第一次initialize方法是初始化父类,后面几个initialize都是方法的调用,即子类没有实现,通过superclass到父类里查找。

你可能感兴趣的:(iOS---12-load_images&类拓展&关联对象原理分析)