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