In our class, if we don’t want to add methods directly to the.m file, which will cause a lot of code, we can use the category to add methods, but when we need to add attributes to the category, we need to use the Runtime association to bind a property to our class object. Now looking at the Runtime implementation, I’m going to focus on a point not mentioned in other articles, which is when we set the two associated objects callback and name in the class

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


We can see this method in action

void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy); 
Copy the code

This function is implemented internally, and we will first look at some data structures inside the Runtime. There is a singleton 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;Copy the code

We can look at the structure of AssociationsHashMap, a c++ class

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); }};Copy the code

So you can see that ObjectAssociationMap is a structure that inherits from a map, and the underlying map is a red-black tree data structure, and AssociationsHashMap is a structure that inherits from unordered_map which is a hash table, So when we call setter methods in the obj object category in our code – (void)setCallBack:(CallBack) CallBack and 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]; }Copy the code

As you can see, he’s actually passing in two arguments, the obj object address and the cb argument to the objc_setAssociatedObject function

objc_setAssociatedObject(obj, @selector(callback), cb, OBJC_ASSOCIATION_COPY_NONATOMIC);
objc_setAssociatedObject(obj2, @selector(name), cb, OBJC_ASSOCIATION_COPY_NONATOMIC);
Copy the code

So we’re going to look it up in the AssociationsHashMap hash table based on obj, and we’re going to return an iterator, and if we find it in the hash table, we’re going to fetch the ObjectAssociationMap, which is a map, His node contains key-value pairs STD ::pair

, the first parameter key is actually our @selector(callback), and the second parameter is the objcassociety structure, which is the value and policy that we pass in

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

AssociationsHashMap(hash table) -> ObjectAssociationMap(map, Bottom red-black tree, time complexity O(logN)) -> objcassociety (store value and policy), so we find the time complexity O(logN) of the association.



Then I drew my own linked data model

objc_setAssociatedObject(obj, @selector(callback), cb, OBJC_ASSOCIATION_COPY_NONATOMIC);
Copy the code

ObjectAssociationMap = ObjectAssociationMap = objectAssociationShashMap Find the object Society (time complexity O(logN)) according to @selector(callback) in ObjectAssociationMap, and then replace or retrieve the value in the Object Society





We look at these two classes, ObjectAssociationMapAllocator and two AssociationsHashMapAllocator malloc and free memory allocator directly with the system function, use the STL memory pool is implemented

pointer allocate(size_type n, const_pointer = 0) {    
   return static_cast<pointer>(::malloc(n * sizeof(T)));
} 
void deallocate(pointer p, size_type) { ::free(p); }Copy the code