iOS关联AssociatedObject一些知识

在我们一个类中如果不希望直接在.m文件添加方法,这样会造成代码量暴涨,我们可以使用分类category去增加方法,但是当我们需要在category中添加属性时候,我们要用到runtime关联去绑定一个属性到我们类对象,现在来看看runtime实现,这里借一张图,我主要介绍下其他文章没有提到的点,当我们在分类设置二个关联对象callback和name

- (void)setCallBack:(CallBack)callback{
    objc_setAssociatedObject(self, @selector(callback), callback, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

- (CallBack)callback
{
    return objc_getAssociatedObject(self, _cmd);
    //    return objc_getAssociatedObject(self, @selector(callback));
}
- (void)setName:(CallBack)name{
    objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

- (CallBack)name
{
    return objc_getAssociatedObject(self, _cmd);
    //    return objc_getAssociatedObject(self, @selector(callback));
}
复制代码


我们可以看到这个方法具体实现

void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy); 
复制代码

这个函数内部实现,具体我们要先看看runtime内部几个数据结构,有一个单例AssociationsHashMap

spinlock_t AssociationsManagerLock;class AssociationsManager {
    // associative references: object pointer -> PtrPtrHashMap.    
static AssociationsHashMap *_map;
public:   
 AssociationsManager()   { AssociationsManagerLock.lock(); }   
 ~AssociationsManager()  { AssociationsManagerLock.unlock(); }     
 AssociationsHashMap &associations() {        
     if (_map == NULL)            
     _map = new AssociationsHashMap();       
     return *_map;    
}};

AssociationsHashMap *AssociationsManager::_map = NULL;复制代码

我们可以看看AssociationsHashMap这个c++类的具体结构

typedef ObjcAllocator<std::pair<void * const, ObjcAssociation> > ObjectAssociationMapAllocator;    class ObjectAssociationMap : public std::map<void *, ObjcAssociation, ObjectPointerLess, ObjectAssociationMapAllocator> {    public:        void *operator new(size_t n) { return ::malloc(n); }        void operator delete(void *ptr) { ::free(ptr); }    };    typedef ObjcAllocator<std::pair<const disguised_ptr_t, ObjectAssociationMap*> > AssociationsHashMapAllocator;    class AssociationsHashMap : public unordered_map<disguised_ptr_t, ObjectAssociationMap *, DisguisedPointerHash, DisguisedPointerEqual, AssociationsHashMapAllocator> {    public:        void *operator new(size_t n) { return ::malloc(n); }        void operator delete(void *ptr) { ::free(ptr); }    };复制代码

所以可以看到ObjectAssociationMap这个结构是继承map,map底层是红黑树的数据结构,而AssociationsHashMap则是继承unordered_map是一个哈希表,所以当我们在代码中调用obj对象category中的setter方法时也即- (void)setCallBack:(CallBack)callback和setName时

- (void)viewDidLoad {
    [super viewDidLoad];
    Obj *obj = [Obj alloc] init];
    [obj setCallBack:cb]; 
    [obj setName:cb];
    Obj *obj2 = [Obj alloc] init];
    [obj2 setCallBack:cb]; 
    [obj2 setName:cb];}复制代码

我们可以看到,他其实会将两个参数传过去,obj对象地址和cb参数传入objc_setAssociatedObject函数中

objc_setAssociatedObject(obj, @selector(callback), cb, OBJC_ASSOCIATION_COPY_NONATOMIC);
objc_setAssociatedObject(obj2, @selector(name), cb, OBJC_ASSOCIATION_COPY_NONATOMIC);
复制代码

首先会根据obj这个对象地址在AssociationsHashMap哈希表中查找, 返回的是一个迭代器,如果在哈希表中查找到,就取出ObjectAssociationMap,这是map,他的节点包含键值对std::pair<void *, ObjcAssociation>,第一个参数key实际就是我们@selector(callback),第二个参数就是ObjcAssociation结构体,这个参数是我们传递进去value和policy

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

复制代码

网上有张图已经画出他们的层次关系,三层之间的包含AssociationsHashMap(哈希表,时间复杂度为O(1)) -> ObjectAssociationMap(map,底层红黑树,查找的时间复杂度O(logN)) -> ObjcAssociation(存储值value和策略policy),所以我们查找关联的时间复杂度O(logN)。


接着我自己画了一个关联数据模型

objc_setAssociatedObject(obj, @selector(callback), cb, OBJC_ASSOCIATION_COPY_NONATOMIC);
复制代码

先根据第一个参数对象地址obj在AssociationsHashMap哈希表中查找到ObjectAssociationMap,在根据ObjectAssociationMap中的@selector(callback)查找到ObjcAssociation,时间复杂度O(logN),然后替换或取出ObjcAssociation中的value值




我们在看看这两个类,ObjectAssociationMapAllocator和AssociationsHashMapAllocator两个内存分配器直接用的系统malloc和free函数,没有用stl内存池的实现方式

pointer allocate(size_type n, const_pointer = 0) {    
   return static_cast<pointer>(::malloc(n * sizeof(T)));
} 
void deallocate(pointer p, size_type) { ::free(p); }复制代码


转载于:https://juejin.im/post/5c3e93d0e51d4551df6f0165

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值