iOS底层探索之路 下

iOS底层第七天 -- 分类Category之关联

2020-04-02  本文已影响0人  三月木头

引导语:
我们能否给分类添加属性呢?
想要回答这个问题,我们先了解一下,声明属性时候系统都具体做了什么?


Q:类声明属性时候,系统都具体做了什么?
整个属性的生成过程在runtime中分为以下几步:

  1. 创建该属性,设置其objc_ivar,通过偏移量和内存占用就可以方便获取。
  2. 生成其getter和setter。
  3. 将属性的ivar添加到类的ivar_list中,作为类的成员变量存在。
  4. 将getter和setter加入类的method_list中。之后可以通过直接调用或者点语法来使用。
  5. 将属性的描述添加到类的属性描述列表中。

Q:分类声明属性,系统都做了什么?
分类声明属性,系统只生成setter和getter方法的声明,但是成员变量、setter和getter方法的实现均没有。

我们如何给分类添加属性呢?
我们研究如何给分类添加成员属性的时候,由分类结构体内数据结构发现,成员属性无法存在,但是set、get方法我们可以手动添加进去。
通过研究需求我发现:我们需要的是一种数据结构,这种结构能保存对应变化的属性值,同时需要保证有个唯一的key值能取出这个属性值。 字典结构可以满足*,我们将对象instance的地址作为key,变化属性值作为value。

Q:为什么不能用字典为分类增加实例变量

小知识点:A文件中定义某个成员int abc;,我们可以在项目其他地方通过 extern int abc; abc = 10;进行更改。如果我们只允许abc在A文件执行,则需要修改此属性staic int abc;

Q:如何给分类关联对象?

void objc_setAssociatedObject(id object, const void * key, id value, objc_AssociationPolicy policy)
// object:当前对象
// key:标记key.这里传入的const void *代表传入&地址
//value :关联属性值
//objc_AssociationPolicy:关联对象策略

关联对象策略

关联对象策略.png

保证关联对象key唯一的其他方法

static void *MyKey = &MyKey;
objc_setAssociatedObject(obj, MyKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
objc_getAssociatedObject(obj, MyKey)

//static char MyKey;
objc_setAssociatedObject(obj, &MyKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
objc_getAssociatedObject(obj, &MyKey)

//使用属性名作为key
//直接使用的@"name"类似的变量是存在常量区的,所以地址会相同
objc_setAssociatedObject(obj, @"property", value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_getAssociatedObject(obj, @"property");

//使用get方法的@selecor作为key
objc_setAssociatedObject(obj, @selector(getter), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
objc_getAssociatedObject(obj, @selector(getter))
objc_getAssociatedObject(self, _cmd);
//_cmd:表示当前方法的selector方法

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

objc4源码解读:objc-references.mm



void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
    // retain the new value (if any) outside the lock.
    ObjcAssociation old_association(0, nil);
    id new_value = value ? acquireValue(value, policy) : nil;
    {
        AssociationsManager manager;
        AssociationsHashMap &associations(manager.associations());
        disguised_ptr_t disguised_object = DISGUISE(object);
        if (new_value) {
            // break any existing association.
            AssociationsHashMap::iterator i = associations.find(disguised_object);
            if (i != associations.end()) {
                // secondary table exists
                ObjectAssociationMap *refs = i->second;
                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).
                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.
            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).
    if (old_association.hasValue()) ReleaseValue()(old_association);
}

核心对象内部:

//AssociationsManager内包含AssociationsHashMap
class AssociationsManager {
    static AssociationsHashMap *_map;
}
//AssociationsHashMap内包含ObjectAssociationMap
class AssociationsHashMap : public unordered_map<disguised_ptr_t, ObjectAssociationMap *, DisguisedPointerHash, DisguisedPointerEqual, AssociationsHashMapAllocator> 
//ObjectAssociationMap内包含ObjcAssociation
class ObjectAssociationMap : public std::map<void *, ObjcAssociation, ObjectPointerLess, ObjectAssociationMapAllocator>

//ObjcAssociation 内包含策略和属性值
class ObjcAssociation {
        uintptr_t _policy;
        id _value;
}
关联对象的原理.png

Q:关联对象是否储存在类对象内存中?
A:不是的

Q:设置关联对象为nil,会发生什么?
A:相当于是移除关联对象,内部会有一个erase操作

Q:如何移除所有关联对象?
移除所有的关联对象
void objc_removeAssociatedObjects(id object)

Q:如果类对象销毁,分类的关联对象会移除么?
A:会的


6.category 分类能否添加成员变量?如果可以,如何给category添加成员变量?

上一篇下一篇

猜你喜欢

热点阅读