Category和关联对象

Category也就是分类,可以在没有类定义的情况下为现有类添加方法、属性、协议,众所周知是不能增加成员变量的,但是是可以通过关联对象给类增加的。

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

可以看出,这里面只有方法、协议、属性列表,并没有存储成员变量的地方。所以就用到了关联对象去存储这些数据。

关联对象主要通过以下三个函数进行实现

//设置
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);
}
//删除
void objc_removeAssociatedObjects(id object) 
{
    if (object && object->hasAssociatedObjects()) {
        _object_remove_associations(object, /*deallocating*/false);
    }
}

先看下objc_AssociationPolicy 策略的定义

typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
    OBJC_ASSOCIATION_ASSIGN = 0,           /**< Specifies a weak reference to the associated object. */
    OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, /**< Specifies a strong reference to the associated object. 
                                            *   The association is not made atomically. */
    OBJC_ASSOCIATION_COPY_NONATOMIC = 3,   /**< Specifies that the associated object is copied. 
                                            *   The association is not made atomically. */
    OBJC_ASSOCIATION_RETAIN = 01401,       /**< Specifies a strong reference to the associated object.
                                            *   The association is made atomically. */
    OBJC_ASSOCIATION_COPY = 01403          /**< Specifies that the associated object is copied.
                                            *   The association is made atomically. */
};

这里分别提供了weak、strong、copy对应的策略。怎么使用这里就不多说明了,主要看下内部是怎么实现的。

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));

    DisguisedPtr<objc_object> disguised{(objc_object *)object};
    ObjcAssociation association{policy, value};

    // retain the new value (if any) outside the lock.
    association.acquireValue();

先判断对象和设置的值是否为空,空的话直接返回。再判断对象是否禁止设置关联对象。DisguisedPtr类的disguised指针,DisguisedPtr是为了是为了躲过内存泄漏工具的检查。然后用policy和valus初始化ObjcAssociation类型对象,再调用对象的acquireValue方法。

class ObjcAssociation {
    uintptr_t _policy;
    id _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;
                }
            }
        }
}

可以看到,这里是根据策略执行value的retain还是copy。

bool isFirstAssociation = false;
    {
        AssociationsManager manager;
        AssociationsHashMap &associations(manager.get());

        if (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);

                    }
                }
            }
        }
    }

1、定义是否是第一次设置关联对象isFirstAssociation = false

2、获取AssociationsManager,然后通过manager获取全局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是static的 Storage获取的,全局唯一的。

3、关联对象如果存在,从AssociationsHashMap中通过对象指针获取ObjectAssociationMap

template <typename... Ts>
std::pair<iterator, bool> try_emplace(const 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, Key, std::forward<Ts>(Args)...);
    return std::make_pair(
             makeIterator(TheBucket, getBucketsEnd(), true),
             true);
}

如果之前设置过,通过查找获取后返回,不存在创建后返回。

4、判断如果是第一次设置关联对象,将isFirstAssociation赋值true

5、再将ObjcAssociation存储到ObjectAssociationMap中,以key作为键值。

6、如果value不存在的时候,通过对象指针从hashmap中获取map,然后再通过key从map中获取object,如果存在就erase,如果map的size为空也erase掉。

    if (isFirstAssociation)
        object->setHasAssociatedObjects();

    // release the old value (outside of the lock).
    association.releaseHeldValue();
}

这部分就比较简单了,如果是第一次设置关联对象,就把对象的 newisa.has_assoc设置为true,方便对象释放的时候同时释放关联对象,最后release旧的value。

再看下objc_getAssociatedObject里面是调用了_object_get_associative_reference方法

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

这里很直观的可以看出,先声明了个ObjcAssociation,然后创建AssociationsManager,再获取AssociationsHashMap,再通过AssociationsHashMap以object为key获取ObjectAssociationMap,再以key获取ObjcAssociation,然后返回获取到的值。

综上可看出,存储结构如下图所示