OC’s memory management method is to use reference counting of objects for maintenance, which simply means who created, who released, who referred to who managed the mechanism. The key functions are as follows:
Retain - reference count + 1 release - reference count - 1 retainCount - reference count alloc - allocate memory, construct ISA dealloc - Free spaceCopy the code
- In order to
Alloc, new, copy, mutableCopy
Created object, reference count +1 - Calling object
retain
Method is followed by its reference count +1 - Calling object
release
Method followed by its reference count of -1 - If an object has a reference count of zero, the system automatically calls
dealloc
Method to release the object
In ARC, the compiler inserts retain, release, autorelase and so on for us at compile time without us having to call them manually. However, MRC requires manual management by the developer.
In MRC, dealloc methods need to manually call super.dealloc methods at the end!
- The parent class may be referenced by other objects
- The final release of this class is done in the parent class
OC The location where the object reference count is stored with the weak reference table
The reference count of an object in OC is after ARM64 and is stored in the last few key bits of the object’s ISA:
uintptr_t has_sidetable_rc : 1;
uintptr_t extra_rc : 19
Copy the code
The extra_RC is used to store the reference count of the object. If the reference count cannot be stored, use the global sideTables to store half of the reference count. The use of extra_RC to store reference counts is skipped. Let’s look directly at SideTable
There isa has_sidetable_rc in isa that marks whether the object has a SideTable to help manage the reference count.
In iOS, the operating system assigns a global static SideTables Map!!!! at APP startup It’s a HashTable.
There are 8 sideTables stored internally in iOS!! Select the corresponding SideTable using a Hash function related to the OC object address:
struct SideTable { spinlock_t slock; // Spin lock RefcountMap refcnts; // RefcountMap is a C++ Map. It is also a key weak_table_t weak_table calculated by the Hash function related to OC object address. // Weakreference table -- weak_table_t weak_TABLE} struct weak_table_t {weak_ENTRY_t * Weak_entries; // Is a HashTable of weak_entry_t; // The number of elements in the array... }; struct weak_entry_t { DisguisedPtr<objc_object> referent; // When a weakly referenced object is released, all Pointers in the referrers are set to nil. Union {weak_referrer_t *referrers; weak_referrer_t *referrers; weak_referrer_t *referrers; // Weak_referrer_t inline_referrers[WEAK_INLINE_COUNT]; // Weak_referrer_t [WEAK_INLINE_COUNT]; . }}Copy the code
Conclusion:
- SideTables is a Map that maintains multiple SideTables
- OC object associated with
SideTable
Maintains a reference count table and a weak reference table for the object - The reference count table is actually a Map and needs to be looked up again with the address of the object to find the real reference count information.
- Weak_table is a global weak reference table, referent is a pointer to the reference object,
referrer
Is the address of the weak pointer, so when the object is freed, all referrer will be set to nil!
SideTables is a HashMap designed by Apple to hold multiple SideTables. In order to reduce the performance penalty caused by locking, it can be simply understood as an array SideTables[8]
OC’s Dealloc method
After having the above knowledge, look at OC dealLOc method!!
When an OC object is called release and the reference count is 0, the dealloc method is called.
- (void)dealloc { _objc_rootDealloc(self); } void _objc_rootDealloc(id obj){ assert(obj); obj->rootDealloc(); } inline void objc_object::rootDealloc(){ if (isTaggedPointer()) return; // fixme necessary? /* select * from isa where free! 1. Whether there is a weak reference pointer to this object. 2. Sidetable */ if (fastPath (isa.nonpointer &&! isa.weakly_referenced && ! isa.has_assoc && ! isa.has_cxx_dtor && ! isa.has_sidetable_rc)) { assert(! sidetable_present()); free(this); } else { object_dispose((id)this); } } id object_dispose(id obj) { if (! obj) return nil; objc_destructInstance(obj); free(obj); return nil; } void *objc_destructInstance(id obj) { if (obj) { // Read all of the flags at once for performance. bool cxx = obj->hasCxxDtor(); bool assoc = obj->hasAssociatedObjects(); // This order is important. if (cxx) object_cxxDestruct(obj); if (assoc) _object_remove_assocations(obj); obj->clearDeallocating(); } return obj; } void objc_object::clearDeallocating() { if (slowpath(! isa.nonpointer)) { // Slow path for raw pointer isa. sidetable_clearDeallocating(); } else if (slowpath(isa.weakly_referenced || isa.has_sidetable_rc)) { // Slow path for non-pointer isa with weak refs and/or side table data. clearDeallocating_slow(); } assert(! sidetable_present()); } void objc_object::clearDeallocating_slow() { assert(isa.nonpointer && (isa.weakly_referenced || isa.has_sidetable_rc)); /* Handle SideTable and get SideTable 1 associated with the object. Clean up weak_TABLE table, mainly set to nil, and then clean up weak_table table 2. */ SideTable &table = SideTables()[this]; table.lock(); if (isa.weakly_referenced) { weak_clear_no_lock(&table.weak_table, (id)this); } if (isa.has_sidetable_rc) { table.refcnts.erase(this); } table.unlock(); } void weak_clear_no_lock(weak_table_t *weak_table, Id referent_id) {// call dealloc object objc_Object *referent = (objc_Object *)referent_id; // Obtain the final hashTable weak_entry_t * pointer! weak_entry_t *entry = weak_entry_for_referent(weak_table, referent); if (entry == nil) { /// XXX shouldn t happen, but does with mismatched CF/objc //printf("XXX no entry for clear deallocating %p\n", referent); return; } // zero out references weak_referrer_t *referrers; size_t count; if (entry->out_of_line()) { referrers = entry->referrers; count = TABLE_SIZE(entry); } else { referrers = entry->inline_referrers; count = WEAK_INLINE_COUNT; } // The referrer pointer is set to nil by iterating over a __weak pointer. for (size_t i = 0; i < count; ++i) { objc_object **referrer = referrers[i]; If (*referrer) {if (*referrer == referent) {if (*referrer == referent) { *referrer = nil; } else if (*referrer) { _objc_inform("__weak variable at %p holds %p instead of %p. " "This is probably incorrect use of " "objc_storeWeak() and objc_loadWeak(). " "Break on objc_weak_error to debug.\n", referrer, (void*)*referrer, (void*)referent); objc_weak_error(); } // Remove the entry from the weak_table. Weak_entry_remove (weak_table, entry); }Copy the code
The logic in the source code is very clear, the general process is as follows:
- After release, the object is called if it needs to be released
dealloc
- Determine the isa of the object, release the associated object in order, CXX destructor
- Determine the weak reference flag bit, process the weak reference pointer in the Weak_table, and then remove the weak_entry associated with the object from the weak reference table
- Determine if the reference count table is in use and process additional reference count tables!!