阅读 27

iOS底层探索--关联对象

小谷底层探索合集

  • 兄弟们。今天探索一波关联对象~

1.关联对象的使用

    1. 我们已经知道:分类中用@property定义的属性,只会生成变量的settergetter方法的声明,不会生成方法的实现。
    1. 那我们可以准备一份测试代码:
@interface LGPerson (LG)

@property (nonatomic, copy) NSString *cate_name;

@end

@implementation LGPerson (LG)

@end
复制代码
    1. 这样的话,如果给cate_name赋值,运行就会崩溃。

但是编译不会,这也说明了 :分类中用@property定义的属性,只会生成变量的settergetter方法的声明,不会生成方法的实现。

    1. 我们修改一下代码:
@interface LGPerson (LG)

@property (nonatomic, copy) NSString *cate_name;

@end

@implementation LGPerson (LG)

- (void)setCate_name:(NSString *)cate_name{
    objc_setAssociatedObject(self, "cate_name", cate_name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

- (NSString *)cate_name{
    objc_getAssociatedObject(self, "cate_name");
}

@end
复制代码

我们给他一个settergetter方法,他就没啥问题了

这也是关联对象使用比较多的地方。

2.关联对象的本质

我们探究下关联对象时怎么实现的。

    1. 在源码工程中,我们可点击 objc_setAssociatedObject 进入方法里面,进行探究

兄弟们,看到源码的方法,感觉和原先的不一样了(苹果又做了调整,我这个还是比较新的,😆)

    1. 有些兄弟们可能没有用过C++,对C++不太了解。不过没有啥关系,点进去看看

    1. 我们来分析下这个些参数
- (void)setCate_name:(NSString *)cate_name{
    objc_setAssociatedObject(self, "cate_name", cate_name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

static void
_base_objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
{
  _object_set_associative_reference(object, key, value, policy);
}
复制代码

参数一:id object : 给哪个对象添加属性,这里要给自己添加属性,用self

参数二:void * key :属性名,根据key获取关联对象的属性的值,在objc_getAssociatedObject中通过次key获得属性的值并返回

参数三:id value : 关联的值,也就是set方法传入的值给属性去保存

参数四:objc_AssociationPolicy policy : 策略,属性以什么形式保存。

    1. 我们可以观察下这些策略有什么:
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. */
};
复制代码

这样大家就明白点了~,这个策略其实就是:属性对应的修饰符

    1. 我们继续深入探究,看看他是个什么结构,怎么存的

    1. 我们看下 acquireValue()方法
    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;
            }
        }
    }
复制代码

点进去之后,发现这个是策略判断,然后有个消息转发,也不是重点~

    1. 那么重点应该就在折叠的代码块了~
{
        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 */
                object->setHasAssociatedObjects();
            }

            /* 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. 我们断点调试输出一波

    1. 我们把这个关联画一下。否则容易懵逼~

    1. 看起来好复杂的样子。我们来捋一捋:找出关键方法
  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);
  }

  iterator makeIterator(BucketT *P, BucketT *E,
                        bool NoAdvance=false) {
    return iterator(P, E, NoAdvance);
  }
复制代码

这个时候需要一波灵魂画师~

    1. 最后了~ 我把这个结构整体画了一波。大家看看可否满意~