This is the seventh day of my participation in the August More text Challenge. For details, see:August is more challenging
Hi 👋
- Wechat: RyukieW
- 📦 Technical article archive
- 🐙 making
My personal project | Mine Elic endless sky ladder | Dream of books |
---|---|---|
type | The game | financial |
AppStore | Elic | Umemi |
preface
We’ve explored everything about categories in depth, and adding associated objects to categories is common, but do you really know anything about associated objects? Do you know how it’s set up and released?
This article is based on objC4-818.2 source code
1. Associated objects
Since the attributes of the class do not produce associated member variables, we need to implement the set/get methods ourselves. As follows:
// .h
@property (nonatomic.copy) NSString *cateName;
// .m
- (void)setCateName:(NSString *)cateName {
objc_setAssociatedObject(self."cateName", cateName, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)cateName {
return objc_getAssociatedObject(self."cateName");
}
Copy the code
So, what do objc_setAssociatedObject and objc_getAssociatedObject do?
Second, the objc_setAssociatedObject
_object_set_associative_reference is actually called
2.1 AssociationsHashMap
Through the breakpoint we found an AssociationsHashMap.
You can see that it is a globally static HashMap of type Storage. Used to hold the association relationships of all associated objects at runtime.
2.2 try_emplace
If the table does not have one, the key-value pair is inserted into the table.
Inserts key,value pair into the map if the key isn’t already in the map. The value is constructed in-place if the key is not in the map, otherwise it is not moved.
One set calls try_emplace twice
auto refs_result = associations.try_emplace(disguised, ObjectAssociationMap{});
- Only used to check whether an associated object exists
key
ObjectAssociationMap{}
It’s empty and you don’t do anything about it
- Only used to check whether an associated object exists
auto result = refs.try_emplace(key, std::move(association));
- Here are the real Settings
value
- Here are the real Settings
Trigger the set
Lookup whether the corresponding associated object already exists in the table by calling LookupBucketFor.
The first call to the set method
Because the associated value does not exist, the insert process is performed
The set method is called the second time
Associated value exists, update
2.3 LookupBucketFor
The algorithm is basically the same as the one used to find the method cache.
Objc_msgSend source code and real machine debugging interpretation
2.4 InsertIntoBucket
Insert table
template <typename KeyArg, typename. ValueArgs>BucketT *InsertIntoBucket(BucketT *TheBucket, KeyArg &&Key, ValueArgs &&... Values) {
TheBucket = InsertIntoBucketImpl(Key, Key, TheBucket);
// first 就是 key
TheBucket->getFirst() = std::forward<KeyArg>(Key);
/ / the second is the Value: :new (&TheBucket->getSecond()) ValueT(std::forward<ValueArgs>(Values)...) ;return TheBucket;
}
Copy the code
Release the associated object
Consider: Is the object dealloc automatically cleaned up and managed?
3.1 dealloc
We start with Dealloc to find:
- dealloc
- _objc_rootDealloc
- rootDealloc
- If there are associated objects
- object_dispose
- objc_destructInstance
- _object_remove_assocations
- To release the associated object
- _object_remove_assocations
- objc_destructInstance
- rootDealloc
- _objc_rootDealloc
void
_object_remove_assocations(id object, bool deallocating)
{
ObjectAssociationMap refs{};
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.get());
AssociationsHashMap::iterator i = associations.find((objc_object *)object);
if(i ! = associations.end()) {
refs.swap(i->second);
// If we are not deallocating, then SYSTEM_OBJECT associations are preserved.
bool didReInsert = false;
if(! deallocating) {for (auto &ref: refs) {
if (ref.second.policy() & OBJC_ASSOCIATION_SYSTEM_OBJECT) {
i->second.insert(ref);
didReInsert = true; }}}if(! didReInsert) associations.erase(i); }}// Associations to be released after the normal ones.
SmallVector<ObjcAssociation *, 4> laterRefs;
// release everything (outside of the lock).
for (auto &i: refs) {
if (i.second.policy() & OBJC_ASSOCIATION_SYSTEM_OBJECT) {
// If we are not deallocating, then RELEASE_LATER associations don't get released.
if (deallocating)
laterRefs.append(&i.second);
} else {
i.second.releaseHeldValue();
}
}
for (auto *later: laterRefs) {
later->releaseHeldValue();
}
}
Copy the code
Four,
The associated objects are actually stored in a global HashMap. The data structure of the original Class is not changed directly