iOS——关联对象学习补充

分类

  • 在分类中添加属性会生成对应的成员变量,会生成对应的setter和getter方法的声明,但是不会生成setter和getter方法的实现。
  • 分类中的可以写@property,会编译通过,但是引用变量会报错。
  • 分类中可以/只能访问原有类中.h中的属性。如果想要访问本类中的私有变量,分类和子类一样,只能通过方法来访问。
  • 在本类和分类有同名方法时,优先调用分类的方法。同名方法调用的优先级为分类 > 本类 > 父类。
  • 如果多个分类中都有和原有类中同名的方法,那么调用该方法的时候执行谁由编译器决定;编译器会执行最后一个参与编译的分类中的方法。
  • 因为分类是会被子类继承的,所以只对NSObjec添加分类,那么所有的OC类均可以调用我们的扩展方法!
  • 对于分类中声明的方法,我们可以不去实现,可以交由子类在需要时进行实现,这点和协议很像。区别是协议是<协议名>,而分类是(分类名)
  • OC分类属于Runtime运行时特性,是OC语言独有的创新,其他编程语言所不具备这样的特性! 类扩展属于编译器特性,在编译阶段就会被添加合并到原类中!
  • 分类不区分类方法与实例方法,本质类加载过程分类属于插入数据,实例方法插入到类中,类方法插入到元类中,所以自己并不会有分元类。

分类方法不会覆盖掉原来类中的方法,而是共存的。但是分类中的方法在前面,原来的类中的方法在后面,调用的时候,就会调用分类中的方法,如果多个分类有同样的方法,后编译的分类会调用。

分类中不能直接添加成员变量

会直接报错Instance variables may not be placed in categories,成员变量不能定义在分类中
这是因为在分类的源码中:

struct category_t {
    const char *name;
    classref_t cls;
    struct method_list_t *instanceMethods;
    struct method_list_t *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);
};

没有存放成员变量的数组,只有属性、协议等

类的加载过程(类的实现)

_objc_init

我们根据之前学的dyld的加载可知:类的加载是在程序启动过程中由dyld动态链接器完成的,在 dyld 完成镜像加载和初始化后,objc_init 会被调用来初始化runtime环境。此时,类和类的方法会被注册到运行时系统中。

void _objc_init(void)
{
    // 保证初始化方法只执行一次
    static bool initialized = false;

    if (initialized) return;

    initialized = true;


    // fixme defer initialization until an objc-using image is found?
    // 读取影响运行时的环境变量,例如,我们可以根据这个方法打印出那些关键key
    environ_init();
    // 关于线程key的锁定,tls(thread_local_storage)。例如,每个线程数据的析构函数
    tls_init();
    // 运行c++静态构造函数。在dyld调用之前,objc自己调用其构造函数
    static_init();
    // 运行时环境初始化,主要是建表
    runtime_init();
    // 异常处理初始化
    exception_init();

#if __OBJC2__
    // 缓存条件初始化
    cache_t::init();

#endif
    // 启动回调机制,通常不做什么,因为所有初始化都是惰性的
    // 但对于某些进程,我们要立马加载trampoline dylib
    _imp_implementationWithBlock_init();
    // 注册dyld与objc的通知,&map_images,加一个&符号是代表同步进行
    // map_images是很耗时的操作,而映射镜像是很重要的必须保证dyld与objc的函数指针同步
    _dyld_objc_notify_register(&map_images, load_images, unmap_image);


#if __OBJC2__

    didCallDyldNotifyRegister = true;

#endif

}

在这个函数中调用的 runtime_init();:

void runtime_init(void)
{
    // 创建分类的表
    objc::unattachedCategories.init(32);
    // 创建已分配的类、元类的表
    objc::allocatedClasses.init();
}
  • 主要是运行时的初始化,开辟类、分类的表空间
  • 主要分为两部分:分类初始化、类的表初始化

_dyld_objc_notify_register

这个在刚刚的objc_init中也有见过,它里面的函数是数据的加载的重点:

  • map_images:映射镜像,将image镜像文件加载进内存时,会触发该函数,在这个过程中会加载类、分类、协议等数据
  • load_images:加载镜像,dyld初始化image会触发该函数,load方法就会在此执行
  • unmap_image:dyld将image移除时会触发该函数

map_images

其源码只调用了map_images_nolock函数。
map_images_nolock 源码调用_read_images开始读取镜像文件

_read_images(读取镜像文件)

该函数才是map_images流程的核心开始。很重要了,它做了很多事情,去读取mach-o,以header开始进行读取。你可以直接理解为读取镜像文件到内存中去。它对类的处理大致为以下流程:
第一次进来的时候去创建一个类表,该类表包含了类名等(不是runtime的,是全局的已经被加载或没被加载的类)。先将所有的类都加到这个大类表中,并且将类的地址和名字进行绑定,也就是给类赋予名字。然后将混乱错误类处理(就是我们删除的类,但是删除后内存中依旧有这块地址没被清除,我们称为futureClass。)
然后修复一些重映射类,就是重映射没被加载进来的,然后对分类进行处理,但是在启动的时候并不会走,也就是说分类加载并不在这时候。
最后对非懒加载类进行处理,里面包含了类的实现。

功能:
该函数总有这么几大块功能:

  1. 修复selector引用
  2. 类的处理
  3. 协议处理
  4. 分类的处理
  5. 非懒加载类的实现(需要设置非懒加载的时候才会进入)
    我们在这里主要分析类的处理和非懒加载类的实现。
    在类的底层分析中我们得知,类的数据存储在rw中,但是我们从内存获取到的数据是ro,所以需要重新构造一下,将ro数据加到rw和rwe中

rw, ro, rwe回顾
这三个结构体是影响类的核心所在。
rw本质class_rw_t,dirtyMemory,它包含了ro与rwe,也就是说,我们平时所用到的方法,协议,属性等都是从这里取出来的,类实现的时候默认是ro数据,要记得rw开辟的时候赋值操作。但是为什么说包含rwe呢?
ro本质class_ro_t,cleanMemory,这是类的干净的东西,其编译时就能确定的,例如类名,成员变量,乃至一些方法、属性等,这些存在磁盘中,所以我们看上面篇章的时候类实现都是先从ro入手就是因为这些不可变,存在磁盘。
rwe本质class_rw_ext_t相当于rw的扩展,因为运行时机制我们可以动态添加方法、属性、协议等以及分类操作都会影响类的结构也就是rw,所以在有这些操作的时候系统就会进行开辟rwe,进行创建。但是又不是每一个类都会有上面操作,所以这些类就不会去开辟rwe,避免去染指类的结构

readClass(读取类信息)

在阅读了前面_read_image的源码后,我们发现它调用了readClass函数。

_read_image调用readClass函数做了两件事:

  • 使用addNameClass函数给类映射类名
  • 使用addClassTableEntry函数添加类和元类到所有类的动态表上

那么readClass中是如何实现的呢?
实际上readClass完成这个任务是通过:**一个是将类名与类映射,一个是将类的地址添加到所有类的动态表中 **
源码:

Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
{
    // 非懒加载类获取类名的方式,但是其实这里已经都能获取了,可以看里面实现方式是从ro读取的
    const char *mangledName = cls->nonlazyMangledName();
    if (missingWeakSuperclass(cls)) {
        // No superclass (probably weak-linked). 
        // Disavow any knowledge of this subclass.
        if (PrintConnecting) {
            _objc_inform("CLASS: IGNORING class '%s' with "
                         "missing weak-linked superclass", 
                         cls->nameForLogging());
        }
        addRemappedClass(cls, nil);
        cls->setSuperclass(nil);
        return nil;
    }
    
    cls->fixupBackwardDeployingStableSwift();
    
    Class replacing = nil;
    if (mangledName != nullptr) {
    // 在这里千万不要以为类实现中的ro赋值于rw的操作是在这。
    // 可以自行验证,你会发现无论是系统还是自己创建的都没有走这里
        if (Class newCls = popFutureNamedClass(mangledName)) {
            // This name was previously allocated as a future class.
            // Copy objc_class to future class's struct.
            // Preserve future's rw data block.

            if (newCls->isAnySwift()) {
                _objc_fatal("Can't complete future class request for '%s' "
                            "because the real class is too big.",
                            cls->nameForLogging());
            }

            class_rw_t *rw = newCls->data();
            const class_ro_t *old_ro = rw->ro();
            memcpy(newCls, cls, sizeof(objc_class));

            // Manually set address-discriminated ptrauthed fields
            // so that newCls gets the correct signatures.
            newCls->setSuperclass(cls->getSuperclass());
            newCls->initIsa(cls->getIsa());

            rw->set_ro((class_ro_t *)newCls->data());
            newCls->setData(rw);
            freeIfMutable((char *)old_ro->getName());
            free((void *)old_ro);

            addRemappedClass(cls, newCls);

            replacing = cls;
            cls = newCls;
        }
    }
    
    if (headerIsPreoptimized  &&  !replacing) {
        // class list built in shared cache
        // fixme strict assert doesn't work because of duplicates
        // ASSERT(cls == getClass(name));
        ASSERT(mangledName == nullptr || getClassExceptSomeSwift(mangledName));
    } else {
        if (mangledName) { //some Swift generic classes can lazily generate their names
            // 将类地址与名字绑定起来
            addNamedClass(cls, mangledName, replacing);
        } else {
            Class meta = cls->ISA();
            const class_ro_t *metaRO = meta->bits.safe_ro();
            ASSERT(metaRO->getNonMetaclass() && "Metaclass with lazy name must have a pointer to the corresponding nonmetaclass.");
            ASSERT(metaRO->getNonMetaclass() == cls && "Metaclass nonmetaclass pointer must equal the original class.");
        }
        // 将绑定后的类加到全局大类表中
        addClassTableEntry(cls);
    }

    // for future reference: shared cache never contains MH_BUNDLEs
    if (headerIsBundle) {
        cls->data()->flags |= RO_FROM_BUNDLE;
        cls->ISA()->data()->flags |= RO_FROM_BUNDLE;
    }
    return cls;
}
  • addNamedClass用来给类添加类名
  • addClassTableEntry用来添加类或元类到表上
addNamedClass(添加类名)

通过NXMapInsert函数将类名映射到类上,类和类名的映射是通过一个表来关联的。以后可以更方便的通过类名获取到类

/***********************************************************************
* addNamedClass
* Adds name => cls to the named non-meta class map.
* Warns about duplicate class names and keeps the old mapping.
* Locking: runtimeLock must be held by the caller
 给一个需要命名的非元类添加映射关系:name=>cls
 如果出现重复的类名,则发出警告,并且使用旧的映射关系
**********************************************************************/
static void addNamedClass(Class cls, const char *name, Class replacing = nil)
{
    runtimeLock.assertLocked();
    Class old;
    if ((old = getClassExceptSomeSwift(name))  &&  old != replacing) {
        inform_duplicate(name, old, cls);

        // getMaybeUnrealizedNonMetaClass uses name lookups.
        // Classes not found by name lookup must be in the
        // secondary meta->nonmeta table.
        addNonMetaClass(cls);
    } else {
        //在这里将类名添加到类的映射中
        NXMapInsert(gdb_objc_realized_classes, name, cls);
    }
    ASSERT(!(cls->data()->flags & RO_META));

    // wrong: constructed classes are already realized when they get here
    // ASSERT(!cls->isRealized());
}
addClassTableEntry(添加到类动态表)

有一个所有类的动态表,这个表包含了所有的类,现在就将这个类添加到表中,如果是元类也要把元类添加进去
添加类的地址到一个表中,此处也是为了更好的查找类,以后可以直接通过类地址查找类
这里添加的表是之前在objc_init中allocatedClasses开辟的空间。

/***********************************************************************
* addClassTableEntry
* Add a class to the table of all classes. If addMeta is true,
* automatically adds the metaclass of the class as well.
* Locking: runtimeLock must be held by the caller.
 将一个类cls添加到所有类的表中
 如果addMeta为true,则自动的将其元类也添加进去
**********************************************************************/
static void
addClassTableEntry(Class cls, bool addMeta = true)
{
    runtimeLock.assertLocked();

    // This class is allowed to be a known class via the shared cache or via
    // data segments, but it is not allowed to be in the dynamic table already.
    /*
     这个类可以通过共享的内存或数据段成为一个已知的类,但是不可以因为早已加入到动态列表而称为已知类
     当然这是因为我们此时要加入,所以也就不能在动态列表中存在了。
     */
    /*
     得到之前分配的动态列表(在objc_init中分配的)
     */
    auto &set = objc::allocatedClasses.get();

    ASSERT(set.find(cls) == set.end());

    //如果不是一个已知类,也就是表中不存在cls,则将其加入
    if (!isKnownClass(cls))
        set.insert(cls);
    //再继续添加其元类
    if (addMeta)
        addClassTableEntry(cls->ISA(), false);
}

realizeClassWithoutSwift(类列表数据的构造过程)

上面只是将类的名称和地址添加到表中,并没有看到rwe的方法列表、协议列表、属性列表、变量列表的构造过程,现在看下它是什么时候构造的。

realizeClassWithoutSwift方法就是对于类实现的核心方法了,任何类(懒加载类,非懒加载类)都要经过这个方法的洗礼,才能实现。
其中最关键的就是对rw的赋值等等,以及本类实现,连带着元类,父类,父类元类等都实现

代码结构: 1、设置rw 2、递归实现父类和元类的继承链 3、附着分类数据

static Class realizeClassWithoutSwift(Class cls, Class previously)
{
    runtimeLock.assertLocked();
    
    class_rw_t *rw;
    Class supercls;
    Class metacls;

    if (!cls) return nil;
    // 如果类已经实现就返回,证明流程错误,里面有个方法会进行assert
    if (cls->isRealized()) {
        validateAlreadyRealizedClass(cls);
        return cls;
    }
    ASSERT(cls == remapClass(cls));

    //将类的data()从ro变为rw--------------------------------
    auto ro = (const class_ro_t *)cls->data();
    // 看看是不是元类
    auto isMeta = ro->flags & RO_META;    
    // 如果是futer class,就是在read_images方法的时候那些混乱类
    if (ro->flags & RO_FUTURE) {
        // This was a future class. rw data is already allocated.
        rw = cls->data();
        ro = cls->data()->ro();
        ASSERT(!isMeta);
        cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
    } else {
        // Normal class. Allocate writeable class data.
        // 此处是类的rw开辟
        rw = objc::zalloc<class_rw_t>();
        // ro 赋值于rw真正地方
        rw->set_ro(ro);
        // 标识类实现
        rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
        // 不用说也知道什么意思
        cls->setData(rw);
    }
    //rw里保存的就是数据信息
    //ro是保存在rw中的,所以需要赋值给rw
    //先将信息从类中获取到,这个类就是macho中读取的类,此时虽然有data(),但这是从MachO中获取的原始数据
    //原始数据其实就是ro,这是类本身的数据,也就是干净内存,所以可以通过强转为class_ro_t
//--------------------------------------------------------
    // 这是类的缓存初始化,就是消息查找的时候那个cache_t
    cls->cache.initializeToEmptyOrPreoptimizedInDisguise();

#if FAST_CACHE_META
    if (isMeta) cls->cache.setBit(FAST_CACHE_META);
#endif

    // Choose an index for this class.
    // Sets cls->instancesRequireRawIsa if indexes no more indexes are available
    cls->chooseClassArrayIndex();

    if (PrintConnecting) {
        _objc_inform("CLASS: realizing class '%s'%s %p %p #%u %s%s",
                     cls->nameForLogging(), isMeta ? " (meta)" : "", 
                     (void*)cls, ro, cls->classArrayIndex(),
                     cls->isSwiftStable() ? "(swift)" : "",
                     cls->isSwiftLegacy() ? "(pre-stable swift)" : "");
    }

    // Realize superclass and metaclass, if they aren't already.
    // This needs to be done after RW_REALIZED is set above, for root classes.
    // This needs to be done after class index is chosen, for root metaclasses.
    // This assumes that none of those classes have Swift contents,
    //   or that Swift's initializers have already been called.
    //   fixme that assumption will be wrong if we add support
    //   for ObjC subclasses of Swift classes.
    // 递归实现父类和元类的继承链rw------------------------------
    // 去实现它的父类
    supercls = realizeClassWithoutSwift(remapClass(cls->getSuperclass()), nil);
    // 去实现它的元类
    metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
//----------------------------------------------------------
// nonpointer_isa,不用关心
#if SUPPORT_NONPOINTER_ISA
    if (isMeta) {
        // Metaclasses do not need any features from non pointer ISA
        // This allows for a faspath for classes in objc_retain/objc_release.
        // 我们看到元类的isa打印是其类的名字,因为这里
        cls->setInstancesRequireRawIsa();
    } else {
        // Disable non-pointer isa for some classes and/or platforms.
        // Set instancesRequireRawIsa.
        bool instancesRequireRawIsa = cls->instancesRequireRawIsa();
        bool rawIsaIsInherited = false;
        static bool hackedDispatch = false;
        //如果说你记得上一章我说的环境变量,OBJC_DISABLE_NONPOINTER_ISA
        // 所以nonpointer_isa标识就是末尾为1,下面只是去设置
        if (DisableNonpointerIsa) {
            // Non-pointer isa disabled by environment or app SDK version
            instancesRequireRawIsa = true;
        }
        else if (!hackedDispatch  &&  0 == strcmp(ro->getName(), "OS_object"))
        {
            // hack for libdispatch et al - isa also acts as vtable pointer
            hackedDispatch = true;
            instancesRequireRawIsa = true;
        }
        else if (supercls  &&  supercls->getSuperclass()  &&
                 supercls->instancesRequireRawIsa())
        {
            // This is also propagated by addSubclass()
            // but nonpointer isa setup needs it earlier.
            // Special case: instancesRequireRawIsa does not propagate
            // from root class to root metaclass
            instancesRequireRawIsa = true;
            rawIsaIsInherited = true;
        }
        // 看这里根据环境变量去设置。不需要关心
        if (instancesRequireRawIsa) {
            cls->setInstancesRequireRawIsaRecursively(rawIsaIsInherited);
        }
    }
// SUPPORT_NONPOINTER_ISA
#endif

    // Update superclass and metaclass in case of remapping
    // 设置其父类,以及isa指向元类
    cls->setSuperclass(supercls);
    cls->initClassIsa(metacls);

    // Reconcile instance variable offsets / layout.
    // This may reallocate class_ro_t, updating our ro variable.
    if (supercls  &&  !isMeta) reconcileInstanceVariables(cls, supercls, ro);

    // Set fastInstanceSize if it wasn't set already.
    cls->setInstanceSize(ro->instanceSize);

    // Copy some flags from ro to rw
    if (ro->flags & RO_HAS_CXX_STRUCTORS) {
        cls->setHasCxxDtor();
        if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) {
            cls->setHasCxxCtor();
        }
    }
    
    // Propagate the associated objects forbidden flag from ro or from
    // the superclass.
    if ((ro->flags & RO_FORBIDS_ASSOCIATED_OBJECTS) ||
        (supercls && supercls->forbidsAssociatedObjects()))
    {
        rw->flags |= RW_FORBIDS_ASSOCIATED_OBJECTS;
    }

    // Connect this class to its superclass's subclass lists
    // 添加关系,如果有父类
    if (supercls) {
        addSubclass(supercls, cls);
    } else {
        addRootClass(cls);
    }

    //附着类别数据rw-----------------------------------------
    // 不要关心参数previously,其实它可以当做是备用参数,对我们来讲无实际意义
    methodizeClass(cls, previously);
    //对类的rwe进行设置
    //将分类信息添加到rwe中
    //肃然这里注释写的是附着分类数据,其实rwe中也会把类的数据也就是ro添加到rwe中
//----------------------------------------------------------

    return cls;
}

小结:

  • 加载到内存中的原始的类的数据格式并不是我们所需要的格式,所以就需要重新设置,其实设置的就是rw
  • 类的实现其实就是将类的bits中的data进行重新赋值,基本上就是给rw进行赋值,包括ro和rwe
  • 而这个data()就是rw,rw包含ro和rwe
  • ro是类本身的数据,包括方法列表、属性列表、协议列表、成员变量列表
  • rwe是分类以及运行时改变的数据,包括方法列表、属性列表、协议列表,还有ro

methodizeClass(附着分类、修复类)

代码结构:1、方法排序;2、添加ro数据到rwe中;3、附着分类数据到rwe中

  • 先获取到ro的基本方法列表
  • 通过prepareMethodLists进行排序,这样方法查找时可以用二分查找法来查找imp
  • 通过attachLists将ro的数据附着到rwe上
  • 属性和协议列表也通过attachLists附着到rwe上
  • 上面是把ro的列表加到了rwe中,还需要通过objc::unattachedCategories.attachToClass将分类的方法列表加到rwe中
  • 列表有三种需要操作的,方法列表、协议列表、属性列表(变量列表不存放到rwe中,所以不需要进行操作)

attachToClass(获取分类附着到类上)

将分类或运行时创建的数据添加到类的rwe上

  • 这里通过get()拿到的是分类中的数据
  • load_images函数将分类数据加载后存放到数组中,在类进行加载时直接拿过来附着
  • 这里调用attachCategories函数传入的分类是这个类当前可以加载的所有分类。

总结

在这里插入图片描述

  • 类加载到内存中,需要重新构造类,构造的就是rw,rw包括rwe和ro
  • 类的处理包括给类映射类名、将类和元类添加到所有类的表中
  • rw的构造过程就是给rw添加ro,并且生成rwe
  • rwe的生成过程是先添加ro的数据,之后获取到分类数据,添加到rwe中
  • ro以及分类中的方法列表均要按照方法选择地址排序,方便后续的二分查找法
  • 在将方法列表添加到类的rwe时,需要倒序插入。后加载分类的在前面,后加载的分类在后面,类的数据在最后。

load的加载过程

类load表:loadable_class

struct loadable_class {
    Class cls;  // may be nil
    IMP method;
};

分类load表:loadable_category

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

load_images(加载镜像)

该方法做了两件事,一个是分类的加载,一个是load的加载和调用

void load_images(const char *path __unused, const struct mach_header *mh)
{
    // 当完成dyld通知注册回调后,这里看函数名意思是加载所有的分类,但是是这样吗?
    // 
    if (!didInitialAttachCategories && didCallDyldNotifyRegister) {
        didInitialAttachCategories = true;
        loadAllCategories();
    }

    // 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);
    }
    // 这个没什么看的,就是去调用load方法了,但是先调用类的,再按顺序调用分类的
    // Call +load methods (without runtimeLock - re-entrant)
    call_load_methods();
}


在这里插入图片描述

分类的加载过程

分类的加载方法load_categories_nolock

static void load_categories_nolock(header_info *hi) {
    // 判断是否存在预优化的类别
    bool hasPreoptimizedCategories = hi->info()->dyldCategoriesOptimized() && !DisablePreattachedCategories;
    // 判断是否存在被覆盖的共享缓存镜像
    bool hasRoot = dyld_shared_cache_some_image_overridden();
    // 判断类别是否包含类属性
    bool hasClassProperties = hi->info()->hasCategoryClassProperties();

    size_t count;

    // 定义处理类别列表的lambda表达式
    auto processCatlist = [&](category_t * const *catlist, bool stubCategories) {
        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) {
                // 类别的目标类丢失(可能是弱链接),忽略该类别
                if (PrintConnecting) {
                    _objc_inform("CLASS: 忽略类别 \?\?\?(%s) %p 目标类丢失", cat->name, cat);
                }
                continue;
            }

            // 处理当前类别
            if (cls->isStubClass()) {
                // 如果是Stub类,类别添加到未关联的类别列表中
                if (cat->instanceMethods ||
                    cat->protocols ||
                    cat->instanceProperties ||
                    cat->classMethods ||
                    cat->protocols ||
                    (hasClassProperties && cat->_classProperties))
                {
                    objc::unattachedCategories.addForClass(lc, cls);
                }
            } else {
                // 将类别注册到目标类中
                if (!didInitialAttachCategories && hasPreoptimizedCategories && 
                    objc::inSharedCache((uintptr_t)cls) && 
                    objc::inSharedCache((uintptr_t)cls->safe_ro()))
                    continue;
                 //将分类附加到一个类或其元类时所采取的步骤。
                if (cat->instanceMethods ||  cat->protocols ||  cat->instanceProperties) {
                    if (cls->isRealized()) {
                        if (slowpath(PrintConnecting))
                            _objc_inform("CLASS: 附加类别 (%s) %p 到类 %s", cat->name, cat, cls->nameForLogging());
                        attachCategories(cls, &lc, 1, ATTACH_EXISTING);
                    } else {
                        if (slowpath(PrintConnecting))
                            _objc_inform("CLASS: 添加未关联的类别 (%s) %p 到类 %s", cat->name, cat, cls->nameForLogging());
                        objc::unattachedCategories.addForClass(lc, cls);
                    }
                }

                if (cat->classMethods  ||  cat->protocols ||  (hasClassProperties && cat->_classProperties)) {
                    if (cls->ISA()->isRealized()) {
                        if (slowpath(PrintConnecting))
                            _objc_inform("CLASS: 附加类别 (%s) %p 到元类 %s", cat->name, cat, cls->nameForLogging());
                        attachCategories(cls->ISA(), &lc, 1, ATTACH_EXISTING | ATTACH_METACLASS);
                    } else {
                        if (slowpath(PrintConnecting))
                            _objc_inform("CLASS: 添加未关联的类别 (%s) %p 到元类 %s", cat->name, cat, cls->nameForLogging());
                        objc::unattachedCategories.addForClass(lc, cls->ISA());
                    }
                }
            }
        }
    };

    // 判断是否需要扫描类别列表
    bool scanCatlist = didInitialAttachCategories || !hasPreoptimizedCategories || hasRoot;
    if (scanCatlist) {
        if (slowpath(PrintPreopt)) {
            _objc_inform("预优化:扫描镜像 %p %s 中的类别 "
                         "- didInitialAttachCategories=%d "
                         "hi->info()->dyldCategoriesOptimized()=%d hasRoot=%d",
                         hi->mhdr(), hi->fname(), didInitialAttachCategories,
                         hi->info()->dyldCategoriesOptimized(), hasRoot);
        }
        processCatlist(hi->catlist(&count), /*stubCategories*/false);
    } else {
        if (slowpath(PrintPreopt)) {
            _objc_inform("预优化:忽略镜像 %p %s 中的预优化类别", hi->mhdr(), hi->fname());
        }
    }

    // 无论如何都要扫描指向stub类的类别列表,因为这些类别不是预优化的
    processCatlist(hi->catlist2(&count), /*stubCategories*/true);
}

这段代码首先判断了是否存在经过dyld(动态链接器)优化的类别、共享缓存镜像中是否有被覆盖的类、类别是否包含类属性
然后定义Lambda函数用于处理类别列表,遍历类别链表,如果类已实现,则使用attachCategories将分类附加到这个类上,同时按需要重建类的方法列表、属性列表等。
如果目标类还没有实现,无法直接附加分类。这时,将这个分类信息保存到 objc::unattachedCategories 中,以便在类实现之后再附加。

从上面的代码我们可以知道:分类的加载的核心代码是attachCategoriesattachToClass

attachCategories

attachCategories的作用是将分类的方法、属性和协议附加到目标类或元类上:

static void
attachCategories(Class cls, const locstamped_category_t *cats_list, uint32_t cats_count,
                 int flags)
{
    // 如果开启了 PrintReplacedMethods 选项,则打印替换的分类信息
    if (slowpath(PrintReplacedMethods)) {
        printReplacements(cls, cats_list, cats_count);
    }

    // 如果开启了 PrintConnecting 选项,则打印附加分类的信息
    if (slowpath(PrintConnecting)) {
        _objc_inform("CLASS: attaching %d categories to%s class '%s'%s",
                     cats_count, (flags & ATTACH_EXISTING) ? " existing" : "",
                     cls->nameForLogging(), (flags & ATTACH_METACLASS) ? " (meta)" : "");
        for (uint32_t i = 0; i < cats_count; i++)
            _objc_inform("    category: (%s) %p", cats_list[i].cat->name, cats_list[i].cat);
    }

    /*
     * 在启动期间,只有少数类有超过 64 个分类。
     * 这使用了少量的栈空间,并避免了 malloc。
     *
     * 分类必须以正确的顺序添加,即从后到前。
     * 为了做到这一点,我们从前到后迭代 cats_list,构建局部缓冲区,然后
     * 在 chunks 上调用 attachLists。attachLists 会将列表添加到前面,因此最终结果
     * 是预期的顺序。
     */
    constexpr uint32_t ATTACH_BUFSIZ = 64;  // 定义缓冲区大小
    struct Lists {
        ReversedFixedSizeArray<method_list_t *, ATTACH_BUFSIZ> methods;    // 存储方法列表
        ReversedFixedSizeArray<property_list_t *, ATTACH_BUFSIZ> properties;  // 存储属性列表
        ReversedFixedSizeArray<protocol_list_t *, ATTACH_BUFSIZ> protocols;  // 存储协议列表
    };
    Lists preattachedLists;  // 预附加分类的列表
    Lists normalLists;  // 正常分类的列表

    bool fromBundle = NO;  // 标记是否来自 bundle
    bool isMeta = (flags & ATTACH_METACLASS);  // 标记是否为元类
    auto rwe = cls->data()->extAllocIfNeeded();  // 获取类的扩展数据

    // 遍历分类列表
    for (uint32_t i = 0; i < cats_count; i++) {
        auto& entry = cats_list[i];

        method_list_t *mlist = entry.cat->methodsForMeta(isMeta);  // 获取分类的实例方法列表或类方法列表
        bool isPreattached = entry.hi->info()->dyldCategoriesOptimized() && !DisablePreattachedCategories;  // 判断是否为预附加分类
        Lists *lists = isPreattached ? &preattachedLists : &normalLists;  // 根据是否为预附加分类选择列表

        if (mlist) {
            if (lists->methods.isFull()) {
                prepareMethodLists(cls, lists->methods.array, lists->methods.count, NO, fromBundle, __func__);  // 准备方法列表
                rwe->methods.attachLists(lists->methods.array, lists->methods.count, isPreattached, PrintPreopt ? "methods" : nullptr);  // 附加方法列表
                lists->methods.clear();  // 清空方法列表
            }
            lists->methods.add(mlist);  // 添加方法列表到列表中
            fromBundle |= entry.hi->isBundle();  // 更新是否来自 bundle 标记
        }

        property_list_t *proplist = entry.cat->propertiesForMeta(isMeta, entry.hi);  // 获取分类的属性列表
        if (proplist) {
            if (lists->properties.isFull()) {
                rwe->properties.attachLists(lists->properties.array, lists->properties.count, isPreattached, PrintPreopt ? "properties" : nullptr);  // 附加属性列表
                lists->properties.clear();  // 清空属性列表
            }
            lists->properties.add(proplist);  // 添加属性列表到列表中
        }

        protocol_list_t *protolist = entry.cat->protocolsForMeta(isMeta);  // 获取分类的协议列表
        if (protolist) {
            if (lists->protocols.isFull()) {
                rwe->protocols.attachLists(lists->protocols.array, lists->protocols.count, isPreattached, PrintPreopt ? "protocols" : nullptr);  // 附加协议列表
                lists->protocols.clear();  // 清空协议列表
            }
            lists->protocols.add(protolist);  // 添加协议列表到列表中
        }
    }

    // 附加分类的辅助函数
    auto attach = [&](Lists *lists, bool isPreattached) {
        if (lists->methods.count > 0) {
            prepareMethodLists(cls, lists->methods.begin(), lists->methods.count,
                               NO, fromBundle, __func__);  // 准备方法列表
            rwe->methods.attachLists(lists->methods.begin(), lists->methods.count, isPreattached, PrintPreopt ? "methods" : nullptr);  // 附加方法列表
            if (flags & ATTACH_EXISTING) {
                flushCaches(cls, __func__, [](Class c){
                    // 常量缓存已经在 prepareMethodLists 中处理了
                    // 如果类仍然是常量的,这里保留
                    return !c->cache.isConstantOptimizedCache();
                });
            }
        }

        rwe->properties.attachLists(lists->properties.begin(), lists->properties.count, isPreattached, PrintPreopt ? "properties" : nullptr);  // 附加属性列表

        rwe->protocols.attachLists(lists->protocols.begin(), lists->protocols.count, isPreattached, PrintPreopt ? "protocols" : nullptr);  // 附加协议列表
    };

    // 附加预附加分类和正常分类
    attach(&preattachedLists, true);
    attach(&normalLists, false);
}

这段代码首先定义用于存储分类中方法、属性和协议的缓冲区。
然后遍历传入的分类列表,对于每个分类,提取方法、属性和协议列表。根据是否为预附加分类选择合适的缓冲区 (preattachedLists 或 normalLists)。如果缓冲区满了,则调用 prepareMethodLists 函数整理缓冲区中的方法列表(比如排序、去重),然后调用rwe->methods.attachLists方法直接将整理好的方法列表附加到类的扩展数据rwe中。然后再清空缓冲区,准备放入后面的分类数据。

分类的懒加载类和非懒加载类

  • 主类是非懒加载,其分类也会标记为非懒加载,提前进行加载
  • 如果分类是非懒加载,会促使主类也变为非懒加载
  • 如果主类和分类都是懒加载,则只会在第一次调用方法时加载
    注:这里说的类的加载指构造rwe。

在这里插入图片描述

分类的使用场合

  • ① 给一个类添加新的方法,可以为系统的类扩展功能。
  • ② 分解体积庞大的类文件,可以将一个类按功能拆解成多个模块,方便代码管理。
  • ③ 创建对私有方法的前向引用:声明私有方法,把 Framework 的私有方法公开等。直接调用其他类的私有方法时编译器会报错的,这时候可以创建一个该类的分类,在分类中声明这些私有方法(不必提供方法实现),接着导入这个分类的头文件就可以正常调用这些私有方法。
  • ④ 向对象添加非正式协议:创建一个 NSObject 或其子类的分类称为 “创建一个非正式协议”。 (正式协议是通过 protocol 指定的一系列方法的声明,然后由遵守该协议的类自己去实现这些方法。而非正式协议是通过给 NSObject 或其子类添加一个分类来实现。非正式协议已经渐渐被正式协议取代,正式协议最大的优点就是可以使用泛型约束,而非正式协议不可以。)

分类总结

  • 对于运行时加载的分类情况:1.类与分类都实现load方法;2.分类有两个以上实现load方法
  • 对于编译时加载的分类情况:1.类实现load方法,分类没有;2.类没有实现,分类仅有一个实现;3.类与分类均没实现
    所以希望不要动不动就实现load方法,很耗时,除了一些必要的操作。我们可以去执行initialize方法。

Q&A

Q:分类的对象方法,类方法都存在哪里?

一个类的所有分类的对象方法放在类对象中,所有分类的类方法存放在元类中

Q:分类的方法是如何添加到类对象方法列表中的?

大概流程

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

Q:Category的实现原理

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

Q:Category和Class Extension的区别是什么?

Class Extension在编译的时候,它的数据就已经包含在类信息中
Category是在运行时,才会将数据合并到类信息中

Q:+load方法调用顺序?

  1. 先调用类的+load方法

1.1按照编译先后顺序调用(先编译,先调用)
1.2先调用父类的+load再调用子类的+load

2.** 再调用分类的+load方法**

2.1按照编译先后顺序调用(先编译,先调用)

// 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();
  • 每个类、分类的+load,在程序运行过程中只调用一次,只有在加载类时候调用一次
  • 不存在分类的+load方法覆盖类的+load方法

Q:load、initialize的调用顺序?

  1. load
    1) 先调用类的load
    a) 先编译的类,优先调用load
    b) 调用子类的load之前,会先调用父类的load
    2) 再调用分类的load
    a) 先编译的分类,优先调用load

  2. initialize
    1> 先初始化父类
    2> 再初始化子类(可能最终调用的是父类的initialize方法)

关联对象实现原理

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

  • AssociationsManager
  • AssociationsHashMap
  • ObjectAssociationMap
  • ObjcAssociation

objc_setAssociatedObject方法

这个方法是用于动态添加关联对象的方法,其实现:

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

在其内部调用了_object_set_associative_reference方法:

_object_set_associative_reference

void
_object_set_associative_reference(id object, const void *key, id value, uintptr_t policy)
{
    // 这段代码用于处理当 object 和 key 为 nil 时的情况。一些代码可能依赖于此不会崩溃。
    // 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};
    // 根据策略和值创建关联对象。
    //四个核心对象之一ObjcAssociation
    ObjcAssociation association{policy, value};

    // 在锁外保留新值(如果有)。
    association.acquireValue();

    bool isFirstAssociation = false;
    {
        // 管理关联对象的管理器。
        //四个核心对象之一AssociationsManager
        AssociationsManager manager;
        // 获取关联对象的哈希表。
        //四个核心对象之一AssociationsHashMap
        AssociationsHashMap &associations(manager.get());

        if (value) {
            // 尝试在哈希表中插入关联对象,如果是首次插入,则返回 true。
            auto refs_result = associations.try_emplace(disguised, ObjectAssociationMap{});//四个核心对象之一AssociationsHashMap
            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;
                // 查找指定 key 的关联对象。
                auto it = refs.find(key);
                if (it != refs.end()) {
                    // 交换并移除旧的关联对象。
                    association.swap(it->second);
                    refs.erase(it);
                    // 如果没有关联对象了,则移除对象的关联对象。
                    if (refs.size() == 0) {
                        associations.erase(refs_it);
                    }
                }
            }
        }
    }

    // 在锁外调用 setHasAssociatedObjects,因为这将调用对象的 _noteAssociatedObjects 方法,
    // 这可能会触发 +initialize 方法,可能会做任意操作,包括设置更多的关联对象。
    if (isFirstAssociation)
        object->setHasAssociatedObjects();

    // 释放旧值(在锁外)。
    association.releaseHeldValue();
}

下面来逐步分析四个核心对象:

AssociationsManager和AssociationsHashMap

class AssociationsManager {
   //声明一个映射值为ObjectAssociationMap的_mapStorage成员变量
    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

typedef DenseMap<DisguisedPtr<objc_object>, ObjectAssociationMap> AssociationsHashMap;

可以看出,在AssociationsManager内部有一个哈希表AssociationsHashMap,在这个哈希表中,键是objc_object(即我们传入的object对象),值是ObjectAssociationMap

ObjcAssociation

class ObjcAssociation {
    uintptr_t _policy;//策略
    id _value; //value值
public:
    ObjcAssociation(uintptr_t policy, id value) : _policy(policy), _value(value) {}
    ObjcAssociation() : _policy(0), _value(nil) {}
    ObjcAssociation(const ObjcAssociation &other) = default;
    ObjcAssociation &operator=(const ObjcAssociation &other) = default;
    ObjcAssociation(ObjcAssociation &&other) : ObjcAssociation() {
        swap(other);
    }

    inline void swap(ObjcAssociation &other) {
        std::swap(_policy, other._policy);
        std::swap(_value, other._value);
    }

    inline uintptr_t policy() const { return _policy; }
    inline id value() const { return _value; }

    inline void acquireValue() {
        if (_value) {
            switch (_policy & 0xFF) {
            case OBJC_ASSOCIATION_SETTER_RETAIN:
                _value = objc_retain(_value);
                break;
            case OBJC_ASSOCIATION_SETTER_COPY:
                _value = ((id(*)(id, SEL))objc_msgSend)(_value, @selector(copy));
                break;
            }
        }
    }

    inline void releaseHeldValue() {
        if (_value && (_policy & OBJC_ASSOCIATION_SETTER_RETAIN)) {
            objc_release(_value);
        }
    }

    inline void retainReturnedValue() {
        if (_value && (_policy & OBJC_ASSOCIATION_GETTER_RETAIN)) {
            objc_retain(_value);
        }
    }

    inline id autoreleaseReturnedValue() {
        if (slowpath(_value && (_policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE))) {
            return objc_autorelease(_value);
        }
        return _value;
    }
};

ObjcAssociation中,保存了我们传入的策略_policy和value值_value,并提供了方法来保留、释放、复制和自动释放这些对象。通过使用这个类,可以安全地管理关联对象的生命周期,并确保在不同操作下的一致性。

_object_set_associative_reference的流程

_object_set_associative_reference的代码中还调用了一个acquireValue函数,这个函数是用于通过对策略的判断返回不同的值:

static id acquireValue(id value, uintptr_t policy) {
    switch (policy & 0xFF) {
    case OBJC_ASSOCIATION_SETTER_RETAIN:
        return objc_retain(value);
    case OBJC_ASSOCIATION_SETTER_COPY:
        return ((id(*)(id, SEL))objc_msgSend)(value, SEL_copy);
    }
    return value;
}

_object_set_associative_reference的代码中:
首先根据我们传入的value经过acquireValue函数处理返回了new_value。
之后创建AssociationsManager manager,得到manager内部的AssociationsHashMapassociations
之后我们看到了我们传入的第一个参数object经过DISGUISE函数被转化为了disguised_ptr_t类型的disguised_object
如果传入的value为空,那么就删除这个关联对象;如果传入的value不为空,那么就尝试在哈希表中插入关联对象,如果是首次插入,则返回 true。
在哈希表插入关联对象的流程:

try_emplace

在哈希表插入关联对象用到了auto refs_result = associations.try_emplace(disguised, ObjectAssociationMap{});这句代码,我们来看看try_emplace

template <typename... Ts>
  std::pair<iterator, bool> try_emplace(KeyT &&Key, Ts &&... Args) {
    BucketT *TheBucket;
    if (LookupBucketFor(Key, TheBucket))
      return std::make_pair(
               makeIterator(TheBucket, getBucketsEnd(), true),
               false); // Already in map.

    // Otherwise, insert the new element.
    TheBucket =
        InsertIntoBucket(TheBucket, std::move(Key), std::forward<Ts>(Args)...);
    return std::make_pair(
             makeIterator(TheBucket, getBucketsEnd(), true),
             true);
  }
template<typename LookupKeyT>
  bool LookupBucketFor(const LookupKeyT &Val,
                       const BucketT *&FoundBucket) const {
    const BucketT *BucketsPtr = getBuckets();
    const unsigned NumBuckets = getNumBuckets();

    if (NumBuckets == 0) {
      FoundBucket = nullptr;
      return false;
    }

首先创建一个空的桶 TheBucket。桶用于在哈希表中查找或存储键值对。
调用 LookupBucketFor,尝试在哈希表中查找与传入的 Key对应的桶。如果找到了桶(即键已经存在),则返回指向该桶的迭代器和 false,表示未添加新的元素。
如果哈希表中没有找到对应的桶,则调用 InsertIntoBucket将新创建的空桶 TheBucket 存入 AssociationsHashMap 中。(此时还没有存入policyvalue,它们在assocition对象中)
然后调用refs.try_emplaceassociation(包含 policy 和 value)与 Key 关联起来,存入 refs。Key存bucket的first,value存second。

  • 关联对象并不存储在被关联对象本身内存中,而是有一个全局统一的 AssociationsManager中
  • 一个实例对象就对应一个ObjectAssociationMap,
  • 而ObjectAssociationMap中存储着多个此实例对象的关联对象的key以及ObjcAssociation,
  • ObjcAssociation中存储着关联对象的value和policy策略

objc_getAssociatedObject

objc_getAssociatedObject用于取出关联对象对应的值_value。

id
objc_getAssociatedObject(id object, const void *key)
{
    return _object_get_associative_reference(object, key);
}
id
_object_get_associative_reference(id object, const void *key)
{
    // 创建一个默认构造的 ObjcAssociation 对象
    ObjcAssociation association{};

    {
        // 创建 AssociationsManager 对象,管理关联对象
        AssociationsManager manager;
        // 获取 AssociationsHashMap 的引用
        AssociationsHashMap &associations(manager.get());

        //在associations哈希表中查找给定对象object的关联对象
        AssociationsHashMap::iterator i = associations.find((objc_object *)object);
        if (i != associations.end()) {
            // 找到对象的关联对象后,获取 ObjectAssociationMap
            ObjectAssociationMap &refs = i->second;
            // 在ObjectAssociationMap中查找指定键的关联对象
            ObjectAssociationMap::iterator j = refs.find(key);
            if (j != refs.end()) {
                // 找到关联对象后,保存其值到 association
                association = j->second;
                // 保留关联对象的返回值
                association.retainReturnedValue();
            }
        }
    }

    // 返回与键关联的对象,并自动释放
    return association.autoreleaseReturnedValue();
}

retainReturnedValueObjcAssociation中的一个内联函数:

inline void retainReturnedValue() {
        if (_value && (_policy & OBJC_ASSOCIATION_GETTER_RETAIN)) {
          //增加 _value 对象的引用计数
            objc_retain(_value);
        }
    }

autoreleaseReturnedValue也是ObjcAssociation中的一个内联函数:

inline id autoreleaseReturnedValue() {
        if (slowpath(_value && (_policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE))) {
          //标记_value自动释放
            return objc_autorelease(_value);
        }
        //返回_value
        return _value;
    }

这段代码中传入的值为我们要访问的关联对象object和用于在AssociationsHashMap中获取对应value的key
首先获取AssociationsManager中的AssociationsHashMap,然后在AssociationsHashMap中查找给定对象object的关联对象,找到后获取其对应的ObjectAssociationMap,然后在ObjectAssociationMap中查找传入的key的关联对象,找到后将其值保存到association并调用retainReturnedValue增加_value的引用计数。最后返回与键关联的对象的_value,并自动释放。

objc_removeAssociatedObjects

objc_removeAssociatedObjects函数用来删除所有关联对象,内部调用了_object_remove_assocations:

void objc_removeAssociatedObjects(id object) 
{
    if (object && object->hasAssociatedObjects()) {
        _object_remove_associations(object, /*deallocating*/false);
    }
}
void _object_remove_associations(id object, bool deallocating)
{
    // 创建一个 ObjectAssociationMap 对象,用于存储关联对象
    ObjectAssociationMap refs{};

    {
        // 创建 AssociationsManager 对象,管理关联对象
        AssociationsManager manager;
        // 获取 AssociationsHashMap 的引用
        AssociationsHashMap &associations(manager.get());

        // 在 associations 哈希表中查找给定对象 object 的关联对象
        AssociationsHashMap::iterator i = associations.find((objc_object *)object);
        if (i != associations.end()) {
            // 找到对象的关联对象后,将其内容交换到 refs 中
            refs.swap(i->second);

            // 如果不是在销毁过程中,SYSTEM_OBJECT 关联对象会被保留
            bool didReInsert = false;
            if (!deallocating) {
                // 遍历所有关联对象
                for (auto &ref: refs) {
                    // 检查是否是 SYSTEM_OBJECT 类型的关联对象
                    if (ref.second.policy() & OBJC_ASSOCIATION_SYSTEM_OBJECT) {
                        // 重新插入到哈希表中
                        i->second.insert(ref);
                        didReInsert = true;
                    }
                }
            }
            // 如果没有重新插入任何对象,则从哈希表中删除该对象的关联
            if (!didReInsert)
                associations.erase(i);
        }
    }

    // 在正常释放后需要释放的关联对象
    SmallVector<ObjcAssociation *, 4> laterRefs;

    // 释放所有的关联对象(在锁外进行)
    for (auto &i: refs) {
        // 检查是否是 SYSTEM_OBJECT 类型的关联对象
        if (i.second.policy() & OBJC_ASSOCIATION_SYSTEM_OBJECT) {
            // 如果不是在销毁过程中,RELEASE_LATER 类型的关联对象不会被释放
            if (deallocating)
                laterRefs.append(&i.second);
        } else {
            // 释放关联对象持有的值
            i.second.releaseHeldValue();
        }
    }
    // 对于需要稍后释放的对象,进行释放操作
    for (auto *later: laterRefs) {
        later->releaseHeldValue();
    }
}

在这段代码中:
首先创建一个 ObjectAssociationMap 类型的局部变量 refs。这个变量用于暂时保存和处理与对象关联的所有关联对象。然后获取AssociationsManager中的AssociationsHashMap,然后在AssociationsHashMap中查找给定对象object的关联对象,找到后获取其对应的ObjectAssociationMap,然后将其内容交换到 refs 中,然后在refs中遍历所有的关联对象,检查遍历到的对象有没有SYSTEM_OBJECT策略,如果有,将其重新插入到 associations 哈希表中;如果没有,就从哈希表中删除该对象的关联。然后在锁外释放所有的关联对象。如果不是在销毁过程中,RELEASE_LATER 类型的关联对象不会被释放。

总的来说

关联对象流程

设值:

  1. 创建一个 AssociationsManager 管理类
  2. 获取唯一的全局静态哈希Map
  3. 判断是否插入的关联值是否存在:
  • 存在走第4步
  • 不存在:关联对象插入空流程
  1. 创建一个空的 ObjectAssociationMap 去取查询的键值对
  2. 如果发现没有这个key就插入一个空的BucketT进去,然后返回
  3. 标记对象存在关联对象
  4. 用当前修饰策略(policy)和值(value)组成了一个ObjcAssociation替换原来BucketT中的空
  5. 标记一下 ObjectAssociationMap的第一次为false

取值:

  1. 创建一个 AssociationsManager 管理类
  2. 获取唯一的全局静态哈希表AssociationsHashMap
  3. 根据DisguisedPtr找到AssociationsHashMap中的 iterator迭代查询器
  4. 如果这个迭代查询器不是最后一个 获取:ObjectAssociationMap(这里有policy和value)
  5. 找到ObjectAssociationMap的迭代查询器获取一个经过属性修饰符修饰的value
  6. 返回value
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值