美文网首页
iOS Category

iOS Category

作者: gaookey | 来源:发表于2021-11-24 11:14 被阅读0次

Category 编译之后的底层结构是 struct category_t,里面存储着分类的对象方法、类方法、属性、协议信息,在程序运行的时候,runtime 会将 Category 的数据,合并到类信息中。

struct category_t {
    const char *name; // 分类的名字
    classref_t cls; // 分类所属的类
    WrappedPtr<method_list_t, PtrauthStrip> instanceMethods; // 对象方法列表
    WrappedPtr<method_list_t, PtrauthStrip> classMethods; // 类方法列表
    struct protocol_list_t *protocols; // 协议列表
    struct property_list_t *instanceProperties; //属性列表
    // Fields below this point are not always present on disk.
    struct property_list_t *_classProperties; // 类属性

    method_list_t *methodsForMeta(bool isMeta) {
        if (isMeta) return classMethods;
        else return instanceMethods;
    }

    property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
    
    protocol_list_t *protocolsForMeta(bool isMeta) {
        if (isMeta) return nullptr;
        else return protocols;
    }
};

Category的加载处理过程

_objc_init
map_images
map_images_nolock

_read_images
remethodizeClass
attachCategories
attachLists

  1. 通过Runtime加载某个类的所有Category数据。
  2. 把所有Category的方法、属性、协议数据,合并到一个大数组中。后面参与编译的Category数据,会在数组的前面 。
  3. 将合并后的分类数据(方法、属性、协议),插入到类原来数据的前面。

+ load方法

_objc_init
load_images

prepare_load_methods
schedule_class_loadschedule_class_load(cls->getSuperclass()); 递归调用, 先添加父类,再添加自己,所以会先调用类的 +load 方法,然后才调用自己的 +load 方法。
add_class_to_loadable_list:将 cls 添加到 loadable_classes 数组的最后面
add_category_to_loadable_list

call_load_methods :调用 +load 方法
call_class_loads :调用类的 +load 方法
call_category_loads :调用分类的 +load 方法
(*load_method)(cls, @selector(load))

// 取出类的 +load 方法。load_method指向+load方法内存地址
load_method_t load_method = (load_method_t)classes[i].method;
// 利用函数地址直接调用+load方法
(*load_method)(cls, @selector(load));

+load 方法是根据方法地址直接调用,并不是经过 objc_msgSend 函数调用。

+load 方法会在 runtime 加载类、分类时调用。每个类、分类的 +load,在程序运行过程中只调用一次

调用顺序:
  1. 先调用类的 +load。按照编译先后顺序调用(先编译,先调用),调用子类的 +load 之前会先调用父类的 +load
  2. 再调用分类的 +load。按照编译先后顺序调用(先编译,先调用)。

+initialize方法

+initialize 方法会在类第一次接收到消息时调用。

先调用父类的 +initialize,再调用子类的 +initialize (先初始化父类,再初始化子类,每个类只会初始化1次)

  • +initialize 是通过 objc_msgSend 进行调用的
  • 如果子类没有实现 +initialize,会调用父类的 +initialize(所以父类的+initialize可能会被调用多次)
  • 如果分类实现了 +initialize,就覆盖类本身的 +initialize 调用

objc_msgSendobjc-msg-arm64.s 文件内)

class_getInstanceMethod
lookUpImpOrNil
lookUpImpOrForward
_class_initialize
callInitialize
objc_msgSend(cls, SEL_initialize)

关联对象

不能直接给 Category 添加成员变量,但是可以间接实现 Category 有成员变量的效果。

关联对象由 AssociationsManager 管理并在 AssociationsHashMap 存储,所有对象的关联内容都在同一个全局容器中。

关联对象相关API:

objc_setAssociatedObject(id  _Nonnull object, const void * _Nonnull key, id  _Nullable value, objc_AssociationPolicy policy)
objc_getAssociatedObject(id  _Nonnull object, const void * _Nonnull key)
objc_removeAssociatedObjects(id  _Nonnull object)

使用方法:

// #import <objc/runtime.h>

- (void)setValue:(int)value {
    // 添加关联对象
    objc_setAssociatedObject( self, @selector(value),  @(value), OBJC_ASSOCIATION_COPY);
}

- (int)value {
    // 获得关联对象
    // 隐式参数 _cmd == @selector(value)
    return  [objc_getAssociatedObject(self, _cmd) intValue];
}    

objc_AssociationPolicy:

objc_AssociationPolicy 对应的修饰符
OBJC_ASSOCIATION_ASSIGN assign
OBJC_ASSOCIATION_RETAIN_NONATOMIC strong, nonatomic
OBJC_ASSOCIATION_COPY_NONATOMIC copy, nonatomic
OBJC_ASSOCIATION_RETAIN strong, atomic
OBJC_ASSOCIATION_COPY copy, atomic

实现关联对象技术的核心对象有:

  • AssociationsManager
  • AssociationsHashMap
  • ObjectAssociationMap
  • ObjcAssociation
image.png image.png
class AssociationsManager {
    AssociationsHashMap &get() {
        return _mapStorage.get();
    }
};
typedef DenseMap<const void *, ObjcAssociation> ObjectAssociationMap;
typedef DenseMap<DisguisedPtr<objc_object>, ObjectAssociationMap> AssociationsHashMap;
class ObjcAssociation {
    uintptr_t _policy;
    id _value; 
};

源码跟踪:

id
objc_getAssociatedObject(id object, const void *key)
{
    return _object_get_associative_reference(object, key);
}

void
objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
{
    _object_set_associative_reference(object, key, value, policy);
}
id
_object_get_associative_reference(id object, const void *key)
{
    ObjcAssociation association{};

    {
        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();
}

void
_object_set_associative_reference(id object, const void *key, id value, uintptr_t policy)
{
    ......
}

相关文章

网友评论

      本文标题:iOS Category

      本文链接:https://www.haomeiwen.com/subject/cwjntrtx.html