preface
After studying the method of classification, there is another important point that is the dynamic loading of attributes by classification. If the classification only writes the attributes without implementing the set and GET methods, the system will report a warning. Then what does the underlying layer do for the attributes of classification? This section mainly combs the classification attribute loading principle, in addition about the basic knowledge of class extension supplement.
Attributes of classification
Normally when we add attributes to a class, we need to implement set and GET methods.
- (* *void**)setCateA_name:(NSString *)cateA_name
{
objc_setAssociatedObject(**self**, @"cateA_name", cateA_name, OBJC_ASSOCIATION_RETAIN);
}
-(NSString *)cateA_name
{
**return** objc_getAssociatedObject(**self**,@"cateA_name");
}
Copy the code
The definition of objc_setAssociatedObject, which is different from one version to the next, is the middle layer, just to make sure that the upper-layer code doesn’t change, and the lower-layer code can be extended.
objc_setAssociatedObject(**id** object, **const* * * *void** *key, **id** value, objc_AssociationPolicy policy)
{
_object_set_associative_reference(object, key, value, policy);
}
Copy the code
The important code then comes in the method of implementation. The parameters from left to right represent the associated person, key, value, and association policy. The associate is the object that is currently associated. The association policy has different types according to the associated data, such as Assign, copy, and so on. DisguisedPtr
disguised{(objc_object *)object}; All associated objects are unified into the form of PTR, unified data structure.
_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));
DisguisedPtr<objc_object> disguised{(objc_object *)object};
ObjcAssociation association{policy, value};
// retain the new value (if any) outside the lock.
association.acquireValue();
**bool** isFirstAssociation = **false* *; { AssociationsManager manager; AssociationsHashMap &associations(manager.get()); 阿鲁纳查尔邦if** (value) { **auto** refs_result = associations.try_emplace(disguised, 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)); 阿鲁纳查尔邦if* * (! result.second) { association.swap(result.first->second); }} * *else** { **auto** refs_it = associations.find(disguised); 阿鲁纳查尔邦if** (refs_it ! = associations.end()) { **auto** &refs = refs_it->second; **auto** it = refs.find(key); 阿鲁纳查尔邦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
The AssociationsHashMap is a global table that stores the hash table associated with all objects. ObjectAssociateionMap is a table where the const void* on the left represents the attribute of the corresponding object and the type of the enclosing value. ObjectAssociation, ObjcAssociation association{policy, value}; The encapsulation structure that this method does.
AssociationsManager manager; — This is a constructor.
AssociationsManager() unlocked, ~AssociationsManager() locked, indicating that only the scope of the space, that is, AssociationsManager Manager; The scope space of the method. static Storage _mapStorage; Belongs to a global static variable and can only be called through the AssociationsManager declaration.
阿鲁纳查尔邦class阿鲁纳查尔邦AssociationsManager { **using** Storage = ExplicitInitDenseMap<DisguisedPtr<objc_object>, ObjectAssociationMap>; 阿鲁纳查尔邦static** Storage _mapStorage;
**public**:
AssociationsManager() { AssociationsManagerLock.lock(); }
~AssociationsManager() { AssociationsManagerLock.unlock(); }
AssociationsHashMap &get() {
**return** _mapStorage.get(); } * *static* * * *void阿鲁纳查尔邦init(){ _mapStorage.init(); }};Copy the code
The overall process
There may be many buckets in the hash table. The first time you enter the hash table, you will determine whether there is a bucket. If there is no bucket, you will insert one. If the value is empty, then erase. If the value is empty, then erase.
Release of an associated object
The life cycle is released with objCET, external method objc_removeAssociatedObjects, internal method _object_remove_assocations, in dealloc.
Class extension supplement
In our usual use of very much, to say a simple point is an anonymous classification, he is written after the statement, before the implementation. Write a class extension and xcRun looks at the compilation and doesn’t find that there is a separate structure for the class extension, so does the class extension have its own method to handle it? By printing ro directly before the class method is inserted in the non-lazy loading mode and finding that the method is loaded in, it shows that the method for the class extension is loaded with the class, even if you write a separate declaration of the class extension, implemented in the main class.