This chapter content

  1. Class extensions
  2. Implementation principles of associated objects

This ZhangMu

  1. A lot of people don’t know what class extensions are, but do you use them all the time
  2. Know how associated objects are implemented

Class extensions

Categories I’m sure many of you are familiar with, and category loading has been discussed. But in an interview, a lot of interviewers ask the difference between extensions and categories. Extensions are what we use all the time: after declaration, before implementation. The ones in red are extensions.

Clang restore view extension

You can see that the members, the attributes are added to the class. As an added bonus, if a class extension has a method declaration and is implemented it will also be added to the method list of the class. And it’s already in RO, it’s already been tested, so I’m not going to show it.

Classification and extension

Just mention one striking difference between the two, and you can think of others

Class: Adding a property to a class generates only setters and getters of member variables, but does not implement methods or member variables.

Extensions: Adding properties to an extension is only a private variable, and adding methods is also a private method.

associations

Remember that you can add properties to a class, but the added properties are problematic (only method declarations, no member variables and method implementations). There are several types of correlation policies, so see for yourself. The API remains the same, but objc’s source method has changed several times. If you look at the latest version, the version I looked at it calls the function _object_set_associative_reference.

And the way that the associative object is implemented is it’s a big hash map which is the general table of the associative. This table contains a key-value hash map.

The main table is AssociationsHashMap {DisguisedPtr< objC_object > : ObjectAssociationMap}. Globally unique, small table of this object

ObjectAssociationMap {const void * : ObjcAssociation}. Represents all associated objects of this object

Source code analysis

As a hint, what is the life cycle of the associated object? It actually follows the life cycle of its object.

Remember the setHook version? Don’t worry about it.

_object_set_associative_reference

It’s the function that gets assigned

void _object_set_associative_reference(id object, const void *key, id value, uintptr_t policy) { // This code used to work when nil was passed for object and key. Some code // probably relies on that to not crash. Check and handle it explicitly. // rdar://problem/44094390 if (! object && ! value) return; if (object->getIsa()->forbidsAssociatedObjects()) _objc_fatal("objc_setAssociatedObject called on instance (%p) of class  %s which does not allow associated objects", object, object_getClassName(object)); // The most important thing is here. // This can be used as object DisguisedPtr< objc_Object > Premiere6.0 {(objc_object *)object}; ObjcAssociation Association {policy, value}; ObjcAssociation association{policy, value}; // retain the new value (if any) outside the lock. OBJC_ASSOCIATION_SETTER_RETAIN and OBJC_ASSOCIATION_SETTER_COPY association. AcquireValue (); bool isFirstAssociation = false; {// This is nothing to create an object AssociationsManager manager; // Obtain the globally unique associated table. AssociationsHashMap &associations(manager.get()); If (value) {// for the first time you can go to the larger table and see if there is a smaller table. Auto refs_result = associations. Try_emplace (Overlap, ObjectAssociationMap{}); if (refs_result.second) { /* it's the first association we make */ isFirstAssociation = true; } /* establish or replace the association */ auto &refs = refs_result.first->second; Auto result = refs.try_emplace(key, STD ::move(association)); auto result = refs.try_emplace(key, STD ::move(association)); if (! result.second) { association.swap(result.first->second); }} else {// Auto refs_it = associations. Find (Overlap); if (refs_it ! = associations.end()) { auto &refs = refs_it->second; auto it = refs.find(key); // If there is no object associated with the small table, the small table will also be destroyed. if (it ! = refs.end()) { association.swap(it->second); refs.erase(it); if (refs.size() == 0) { associations.erase(refs_it); } } } } } // Call setHasAssociatedObjects outside the lock, since this // will call the object's _noteAssociatedObjects method if it // has one, and this may trigger +initialize which might do // arbitrary stuff, including setting more associated objects. if (isFirstAssociation) object->setHasAssociatedObjects(); // release the old value (outside of the lock). association.releaseHeldValue(); }Copy the code

_object_get_associative_reference

The function that’s evaluated, it doesn’t matter, it’s evaluated from the table, right

id _object_get_associative_reference(id object, const void *key) { ObjcAssociation association{}; { AssociationsManager manager; AssociationsHashMap &associations(manager.get()); AssociationsHashMap::iterator i = associations.find((objc_object *)object); if (i ! = associations.end()) { ObjectAssociationMap &refs = i->second; ObjectAssociationMap::iterator j = refs.find(key); if (j ! = refs.end()) { association = j->second; association.retainReturnedValue(); } } } return association.autoreleaseReturnedValue(); }Copy the code