SoFunction
Updated on 2025-04-12

Detailed explanation of iOS associated object example

background

In iOS development, if we want to dynamically add attributes to an object or attributes to category, we all implement them through the runtime associated object, how do we access the attributes we add? Is it added directly to the object's own memory? With these questions, let's look at the source code of runtime and untie the mystery of the associated object.

Related object source code

Save value

void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) {
 _object_set_associative_reference(object, (void *)key, value, policy);
}

When we call this method, we passed four parameters in total:

Parameter name explain
id object Objects that need to be associated
void *key Corresponding key
id value The corresponding value
objc_AssociationPolicy policy Memory management policy

Memory management policy:

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. */
};

After understanding the four parameters, let's take a look at its real implementation function_object_set_associative_reference

void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
 // retain the new value (if any) outside the lock.
 ObjcAssociation old_association(0, nil);
 id new_value = value ? acquireValue(value, policy) : nil;
 {
 AssociationsManager manager;
 AssociationsHashMap &amp;associations(());
 disguised_ptr_t disguised_object = DISGUISE(object);//Get the object address if (new_value) {
  // break any existing association.
  AssociationsHashMap::iterator i = (disguised_object);//First get the hashmap of the object through the address of the object  if (i != ()) {//Judge whether it already exists  // secondary table exists
  ObjectAssociationMap *refs = i-&gt;second;//Get the value, the corresponding map  ObjectAssociationMap::iterator j = refs-&gt;find(key);// Find via key  if (j != refs-&gt;end()) {//If it already exists   old_association = j-&gt;second;//Get the original old value so that it can be released later   j-&gt;second = ObjcAssociation(policy, new_value);//Store new value  } else {//Not exists   (*refs)[key] = ObjcAssociation(policy, new_value);
  }
  } else {//If it does not exist, create a  // create the new association (first time).
  ObjectAssociationMap *refs = new ObjectAssociationMap;
  associations[disguised_object] = refs;
  (*refs)[key] = ObjcAssociation(policy, new_value);
  object-&gt;setHasAssociatedObjects();
  }
 } else {//Create a  // setting the association to nil breaks the association.
  AssociationsHashMap::iterator i = (disguised_object);
  if (i != ()) {
  ObjectAssociationMap *refs = i-&gt;second;
  ObjectAssociationMap::iterator j = refs-&gt;find(key);
  if (j != refs-&gt;end()) {
   old_association = j-&gt;second;
   refs-&gt;erase(j);
  }
  }
 }
 }
 // release the old value (outside of the lock).
 if (old_association.hasValue()) ReleaseValue()(old_association);
}

Through the above code, we can see that when the associated object is stored, an AssociationsManager singleton object is generated, so all management objects in the application are stored in this AssociationsManager.

The specific storage implementation is implemented with the help of C++'s associated container unordered_map. For details, please refer to the comments I added in the code.

The whole process is to store something similar to a hashmap through the address of the object object; take this hashmap, and then store the value we need to store in this hashmap through key-value pairs. If there is an old value in this process, the old value will be released in the end.

Get the value

The process of obtaining values ​​is actually relatively simple, which is equivalent to taking values ​​from a hashmap

id objc_getAssociatedObject(id object, const void *key) {
 return _object_get_associative_reference(object, (void *)key);
}
id _object_get_associative_reference(id object, void *key) {
 id value = nil;
 uintptr_t policy = OBJC_ASSOCIATION_ASSIGN;
 {
 AssociationsManager manager;
 AssociationsHashMap &associations(());
 disguised_ptr_t disguised_object = DISGUISE(object);
 AssociationsHashMap::iterator i = (disguised_object);
 if (i != ()) {
  ObjectAssociationMap *refs = i->second;
  ObjectAssociationMap::iterator j = refs->find(key);
  if (j != refs->end()) {
  ObjcAssociation &entry = j->second;
  value = ();
  policy = ();
  if (policy & OBJC_ASSOCIATION_GETTER_RETAIN) {
   objc_retain(value);
  }
  }
 }
 }
 if (value && (policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE)) {
 objc_autorelease(value);
 }
 return value;
}

Summarize

The above is the entire content of this article. I hope that the content of this article has a certain reference value for everyone's study or work. If you have any questions, you can leave a message to communicate. Thank you for your support.