分类的属性探究 & 类扩展补充

492 阅读3分钟

前言

研究完了分类的方法,那么还有一个重要的点就是分类对于属性的动态加载,如果只是写了属性,没有实现set、get方法系统是会报警告的,那么底层对于分类的属性做了什么处理呢?本节主要梳理分类的属性加载原理,另外关于类扩展的基本知识点补充。

分类的属性

正常我们往分类里添加属性,需要实现set、get方法。

-(**void**)setCateA_name:(NSString *)cateA_name

{
    objc_setAssociatedObject(**self**, @"cateA_name", cateA_name, OBJC_ASSOCIATION_RETAIN);
}

-(NSString *)cateA_name

{
    **return** objc_getAssociatedObject(**self**,@"cateA_name");
}

关于objc_setAssociatedObject的定义,每个版本都不一样,这里属于中间层,为的就是保证上层的代码不会变,下层的可以扩展。

objc_setAssociatedObject(**id** object, **const** **void** *key, **id** value, objc_AssociationPolicy policy)

{
    _object_set_associative_reference(object, key, value, policy);
}

那么重点的代码就来到了实现的方法。从左往右的参数依次代表了被关联者keyvalue关联策略。被关联者就是当前关联的是哪个对象,关联策略根据关联数据有不同的类型,比如assign、copy等等。DisguisedPtr<objc_object> disguised{(objc_object *)object};把所有的关联对象统一封装成ptr的形式,统一数据结构。

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

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

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

}

上面的AssociationsHashMap这是一张全局的表,存储了所有的对象关联的另一张hash表objectAssociateionMap,这张表里左边的const void*表示的对应的对象的属性,所对应的封装好的值的类型,结构就是objectAssociation,也就是ObjcAssociation association{policy, value};这个方法所做的封装结构。

截屏2021-07-22 上午10.09.19.png

AssociationsManager manager; --- 这是一个构造函数。

AssociationsManager()函数开锁,~AssociationsManager()上锁,表示只存在该作用域的空间,也就是 AssociationsManager manager;所在方法的作用域空间。static Storage _mapStorage;属于全局静态变量,并且只能通过AssociationsManager声明调用。

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

    }

};

总体流程

根据对象找到关于这个对象的hash表,里面可能会有很多的bucket,第一次进来就会判断有没有bucket,如果没有bucket就会插入一个,bucket里面有很多关于这个类的信息(属性...),bucket的扩容同之前的方法扩容一样,然后去找是关于哪个关联对象的值,如果传值传空,就会调erase,清空。

关联对象的释放

生命周期随着objcet的释放而释放,对外方法objc_removeAssociatedObjects,对内方法_object_remove_assocations,在dealloc里面.

类扩展补充

在我们平时使用中非常的多,说的简单一点就是一个匿名的分类,他是写在声明后,实现前。写一个类扩展并且xcrun看一下编译情况,并没有发现类扩展有单独的结构体,那么类扩展有自己的方法处理呢?通过在非懒加载模式下在分类方法插入之前直接打印ro,发现已经把方法加载进去了,说明类扩展的方法是伴随着类一起加载进去的,即使你单独写一个类扩展的声明,在主类中实现,也是一样的。