@[toc]
Retain, release review
We’re in the Strong implementation; So now that we’ve looked at the source code for Retain and Release let’s take a look at this and just review it
Release should be
See this blog [iOS Developer]ARC for more details
It is clear that there are two ways to store reference counts, one through ISA and the other through SideTable
SideTable
HashMap is a data structure based on an array. Through a certain algorithm, the key is calculated to produce a number. The number is used as the index of the array, and the value is stored in the memory corresponding to the index
It’s possible for a HashTon algorithm to produce duplicate numbers. How do you store data with duplicate hash values in a hash table? The common methods are closed hash and open hash, and the hash table using open hash is called hash bucket. == Open hash is to use a linked list or array at the corresponding position of the hash value, and store the data of hash value conflict into the linked list or array to improve the lookup efficiency ==
To manage reference counts and weak Pointers for all objects, Apple creates a global SideTables, which, despite the name “s”, is nothing more than a global Hash bucket containing the SideTable structure ==. It uses the memory address of the object as its key. == to manage reference counts and weak Pointers ==.
Take a look inside the SideTable
struct SideTable {
spinlock_t slock; / / the spin lock
RefcountMap refcnts; // Store the reference count
weak_table_t weak_table; // Weak_table is a hash
Copy the code
Learn about the three member variables in SideTable
Spinlock_t slock Spin lock
The lock
We learned that operating system locks are an important tool for thread synchronization and there are five major locks in the operating system
- Semaphore:
-
- Integer semaphore S,S<=0 indicates that the resource is occupied,S >0 indicates that the resource is available, pv operation access
-
- Recorded semaphore S. value > 0 indicates the number of resources available; < 0 indicates the number of blocks that have been blocked in the waiting list
-
- An AND semaphore is a semaphore operation that requires multiple resources simultaneously AND consumes one of each.
-
- A semaphore set pair should have multiple resources, equivalent to a recordic set
- Mutex: Similar to binary semaphores, except that mutex must be acquired and released in the same thread. It is invalid if a thread releases a semaphore that it does not own. Semaphores can be released by other threads.
- Critical section: In a concurrent execution process, a section of a program that accesses critical resources and must be executed mutually exclusive is called a critical section
- Read/write lock: a lock that resolves the reader writer problem
- Condition variables: A condition variable acts as a notification mechanism. Multiple threads can set the wait condition variable, and once another thread sets the condition variable (equivalent to the wake condition variable), multiple waiting threads can continue execution.
Separate lock, split lock
Because == object reference counting related operations should be atomic ==. Otherwise, if multiple threads write the reference count of an object at the same time, it will cause data confusion and lose the meaning of memory management. At the same time, because the number of objects in memory is large and SideTables needs to be operated on very frequently, the entire Hash table cannot be locked. == Apple uses separate lock technology ==
- Lock splitting and lock striping are two ways to reduce lock contention by reducing the frequency with which threads request locks. Independent state variables should be protected with independent locks. But sometimes developers make the mistake of using a single lock to protect all state variables. These locks need to be carefully allocated to reduce the risk of deadlocks
- If a lock guards multiple independent state variables, you might be able to split the lock so that each one guards a different variable. This makes each lock less frequently requested. For moderately competitive locks, split locks effectively convert most of them into non-competitive locks, resulting in improved performance and scalability.
- A split lock can sometimes be extended into a collection of locked blocks that belong to separate objects, in this case a split lock.
We add one lock to the reference count of each object in each SideTable. This is called a split lock, which is safe but expensive
We add a lock to each SideTable so that only one SideTable cannot be accessed more than once. This is called a separate lock
spinlocks
Spin locks and mutexes
- Similarities: Only one thread can access a shared resource at a time. Both are thread safe.
- Difference:
-
- Mutex: If the shared data has been locked by another thread, the thread will hibernate and wait for the lock. Once the accessed resource is unlocked, the thread waiting for the resource is awakened.
-
- Spin-lock: If the shared data is already locked by another thread, the thread waits for the lock in an infinite loop. Once the accessed resource is unlocked, the thread waiting for the resource executes immediately.
- Spinlocks are more efficient than mutex locks. However, it is important to note that since spin does not release the CPU, the thread holding the spin lock should release the spin lock as soon as possible, otherwise the thread waiting for the spin lock will always spin there, which will waste CPU time.
- The SideTable is locked during reference counting operations to avoid data errors
Apple’s Choice
For each SideTable, there is a spin lock in the middle and a separate lock is used to lock a single SideTable
Safety + efficiency is reasonable
RefcountMap
So let’s look at this diagram
The HASH table with DisguisedPtr< objC_Object > as the key is used to store the reference count of the OC object. We don’t know what DisguisedPtr< objC_Object > is, but we have been quite clear about the way the reference count is stored in the retain. If ISA optimization is not turned on or if the EXTRA_RC reference count for ISA_T overflows after being incremented by isa optimization, it will be stored in this hash table.
weak_table_t weak_table
A hash table that stores a weak reference pointer to an object. The weak function implements the core data structure.
Look at the wewak_table_t
struct weak_table_t {
weak_entry_t *weak_entries; // Continuous address space header pointer, array
// Manages all weak Pointers to an object, which is also a hash
size_t num_entries; // The number of occupied positions in the array
uintptr_t mask; // Array index Max (array size -1)
uintptr_t max_hash_displacement; // The maximum hash offset
};
Copy the code
Weak_table_t does not store the weak pointer directly through the array, but stores the two parameters of the weak pointer through the structure
- Location: the address of the __weak pointer, which stores the address of the pointer so that we can finally set the object to nil
- NewObj: The referenced object
struct weak_entry_t {
DisguisedPtr<objc_object> referent; // The address of the object to be referred to. The previous loop-through search is to determine whether the target address is equal to him.
union {
struct {
weak_referrer_t *referrers; // A mutable array that holds the addresses of all weak references to this object. When this object is freed, all the Pointers in the referrers are set to nil.
// The purpose of the hash is to clear an weak pointer
// An array of weak Pointers to the referent object
uintptr_t out_of_line_ness : 2; // If the inline boundary is exceeded, it will be mentioned below
uintptr_t num_refs : PTR_MINUS_2; // The size already occupied in the array
uintptr_t mask; // Array index Max (array size -1)
uintptr_t max_hash_displacement; // The maximum hash offset
};
struct {
// out_of_line_ness field is low bits of inline_referrers[1]
weak_referrer_t inline_referrers[WEAK_INLINE_COUNT]; // A 4-element array is used by default to store Pointers to weak references. Use referrers to store Pointers when there are more than 4.
// If there are no more than four weak Pointers to the object, use the array inline_referrers instead of hhash
};
};
Copy the code
The Union Commons is also a reminder that Apple uses the same memory to store different information
There are two arrays in the middle, weak_referrer_t *referrers and weak_referrer_t inline_referrers[WEAK_INLINE_COUNT]; If the weak pointer number is less than 4, it will be stored in the second array, which saves the hash and improves the storage efficiency. If the weak pointer number is greater than 4, it will be stored in the Referrers
Constructors and destructors
// The constructor
SideTable() {
memset(&weak_table, 0.sizeof(weak_table));
}
// Fatal = fatal; // Fatal = fatal;
~SideTable() {
_objc_fatal("Do not delete SideTable.");
}
Copy the code
So the SideTable🈲️ destructor (the purpose of the destructor is not to delete the object, but to do some cleaning before the memory occupied by the object is destroyed).
Finally, there is the operation of the lock
void lock() { slock.lock(); }
void unlock() { slock.unlock(); }
void forceReset() { slock.forceReset(); }
// Address-ordered lock discipline for a pair of side tables.
template<HaveOld, HaveNew>
static void lockTwo(SideTable *lock1, SideTable *lock2);
template<HaveOld, HaveNew>
static void unlockTwo(SideTable *lock1, SideTable *lock2);
};
Copy the code
A little summary of SideTable
Weak parts
Let’s look at the assemblyThere are only two parts to it
Let’s take a look at the corresponding source section
objc_initWeak
objc_initWeak(id *location, id newObj)
{
if(! newObj) { *location =nil;
return nil;
}
return storeWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating>
(location, (objc_object*)newObj);
}
Copy the code
An invalid object directly results in the release of the pointer
If it does, then the objc_storeWeak() function is called
objc_storeWeak
Look at the comment above the store function.
Update the weak variable if haveOld istrue, then the variable has an old value that needs to be cleaned up. This value may benil[theweakThe pointer already has a point to] if haveNew istrue, a new value needs to be assigned to the variable, which may benilIf CrashIfDeallocating fortrueAnd if newObj is being unallocated or newObj's class does not support weak references, the process stops if CrashIfDeallocating isfalse, the storenil
Copy the code
Let’s take a look at the specific logic and judgment
template <HaveOld haveOld, HaveNew haveNew, CrashIfDeallocating crashIfDeallocating> static id storeWeak(id *location, objc_object *newObj) { assert(haveOld || haveNew); if (! haveNew) assert(newObj == nil); / / the procedure used to update a weak reference pointer pointing to / / initialization previouslyInitializedClass pointer Class previouslyInitializedClass = nil; id oldObj; SideTable *oldTable; SideTable *newTable; // The template functions haveOld and haveNew are passed in by the compiler. Location is the weak pointer and newObj is the object to be retry: OldObj = *location; if (haveOld) {// change the pointer to oldObj = *location; oldTable = &SideTables()[oldObj]; } else { oldTable = nil; } if (haveNew) {newTable = &sidetables ()[newObj]; newTable = &sidetables ()[newObj]; } else { newTable = nil; } SideTable::lockTwo<haveOld, haveNew>(oldTable, newTable); SideTable::lockTwo<haveOld, haveNew>(oldTable, newTable); If (haveOld && *location!); // If (haveOld && *location!); // If (haveOld && *location! = oldObj) { SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable); goto retry; } // Prevent deadlocks between weak references // and ensure that all weak references to ISA are non-null with the +initialize constructor if (haveNew && newObj) {// get the new object's ISA pointer Class CLS = newObj->getIsa(); If (CLS! = previouslyInitializedClass && ! ((objc_class *)cls)->isInitialized()) { SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable); // Initialize its ISA pointer. _class_initialize(_class_getNonMetaClass(CLS, (id)newObj)); // If the class has completed execution +initialize is ideal // If the class + Initialize is in the thread // for example +initialize is calling storeWeak // you need to manually add protection policies to it. Mark previouslyInitializedClass = CLS and set previouslyInitializedClass pointer; // Retry goto retry; } } // Clean up old value, if any. if (haveOld) { weak_unregister_no_lock(&oldTable->weak_table, oldObj, location); } if (haveNew) {// If weak is going to point to a new value, NewObj = (objC_object *) weak_register_no_lock(&newTable-> Weak_table, (id)newObj, location, crashIfDeallocating); // The weak_register_no_lock method returns nil if the weak_register_no_lock method is released. // The weak_register_no_lock method returns nil if the weak_register_no_lock method is released. NewObj ->isTaggedPointer() {newObj-> setLyReferenced_nolock (); newObj->isTaggedPointer(); *location = (id)newObj; *location = (id)newObj; } SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);} SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable); return (id)newObj; }Copy the code
storeWeak
By accepting three parametersHaveOld, haveNew and crashIfDeallocation
, the three arguments are passed in as template functions.-
- HaveOld: Whether the weak pointer previously pointed to a weak reference
-
- HaveNew: Weak indicates whether the pointer needs to point to a new reference
-
- If the weakly referenced object is being destroyed, then the weak reference to the object should crash
-
- The value we pass in when we initialize is 0, 1, 1
- It maintains two tables
oldTable
andnewTable
The two tables represent the old weak reference table and the new weak reference table, and they are bothSideTable
The hash table - Is called if the weak pointer previously pointed to a weak reference
weak_unregister_no_lock
Method to remove the old weak pointer address - If the weak pointer needs to point to a new reference
-
- Will call
weak_register_no_lock
Method to add a new weak pointer address to the weak reference table
- Will call
-
- If the weak_register_no_lock method where the weak reference was freed returns nil, the weak reference flag bit is set in the reference count table
Weak_register_no_lock adds the new weak pointer to the weak reference table
id
weak_register_no_lock(weak_table_t *weak_table, id referent_id,
id *referrer_id, bool crashIfDeallocating)
{
objc_object *referent = (objc_object *)referent_id;
objc_object **referrer = (objc_object **)referrer_id;
if(! referent || referent->isTaggedPointer())return referent_id;
// ensure that the referenced object is viable
bool deallocating;
if(! referent->ISA()->hasCustomRR()) { deallocating = referent->rootIsDeallocating(); }else {
BOOL (*allowsWeakReference)(objc_object *, SEL) =
(BOOL(*)(objc_object *, SEL))
object_getMethodImplementation((id)referent,
SEL_allowsWeakReference);
if ((IMP)allowsWeakReference == _objc_msgForward) {
return nil;
}
deallocating =
! (*allowsWeakReference)(referent, SEL_allowsWeakReference);
}
if (deallocating) {
if (crashIfDeallocating) {
_objc_fatal("Cannot form weak reference to instance (%p) of "
"class %s. It is possible that this object was "
"over-released, or is in the process of deallocation.",
(void*)referent, object_getClassName((id)referent));
} else {
return nil; }}// now remember it and where it is being stored
weak_entry_t *entry; // If weak_table has corresponding entry
if ((entry = weak_entry_for_referent(weak_table, referent))) {// Returns a weak reference entry for the given reference object. If the Referent has no entry, NULL is returned. Perform a lookup.
append_referrer(entry, referrer); Add the given reference to the weak pointer set in the entry. No double checking is performed (no b/ C weak Pointers are added to the collection twice).
}
else {
weak_entry_t new_entry(referent, referrer); // Create a new entry
weak_grow_maybe(weak_table); // Check whether the size of weak_entries array in Weak_TABLE needs to be adjusted
weak_entry_insert(weak_table, &new_entry); // Insert the new entry into the weak_table, official: add the new _ entry to the weak reference table of the object. Does not check whether the referenced object is already in the table.
}
// Do not set *referrer. objc_storeWeak() requires that the
// value not change.
return referent_id;
}
Copy the code
- Four parameters are passed in
-
weak_table
:weak_table_t
Structure types we’ve seen before
-
referent_id
: Weak indicates an object to which the weak pointer points
-
*referrer_id
: The address of the weak pointer, which is needed for operations
-
crashIfDeallocating
: If the weakly referenced object is being destroyed, then the weak reference to the object should crash
- The main process
- If the referent is nil or the referent is TaggedPointer, it just returns, and it doesn’t do anything
- If destructing, throw an exception
- If the object cannot be referred to by weak, return nil
- If the object is not one of the above cases, then
weak_entry_for_referent
Method, according to the address of the weak reference object from the weak reference table to find the corresponding weak_entry (pointer to its object), if it can find the callappend_referrer
Method inserts the weak pointer address into it. Otherwise, create a new weak_entry
The core has two corresponding functions
Take weak_entry_for_referent elements
static weak_entry_t *
weak_entry_for_referent(weak_table_t *weak_table, objc_object *referent)
{
assert(referent);
weak_entry_t *weak_entries = weak_table->weak_entries;
if(! weak_entries)return nil;
size_t begin = hash_pointer(referent) & weak_table->mask; // Here we use the bit operation of &weak_table ->mask to ensure that index does not cross the boundary
size_t index = begin;
size_t hash_displacement = 0;
while(weak_table->weak_entries[index].referent ! = referent) { index = (index+1) & weak_table->mask;
if (index == begin) bad_weak_table(weak_table->weak_entries); // Bad Weak table crash is triggered
hash_displacement++;
if (hash_displacement > weak_table->max_hash_displacement) { // Return nil when hash collisions exceed possible Max hash collisions, indicating that the element is not in the hash table
return nil; }}return &weak_table->weak_entries[index];
}
Copy the code
Append_referrer Adds the element
static void append_referrer(weak_entry_t *entry, objc_object **new_referrer)
{
if (! entry->out_of_line()) { // If dynamic array is not used by weak_entry, go here
// Try to insert inline.
for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
if (entry->inline_referrers[i] == nil) {
entry->inline_referrers[i] = new_referrer;
return; }}// If the inline_referrers location is full, convert it to the referrers and make a dynamic array.
// Couldn't insert inline. Allocate out of line.
weak_referrer_t *new_referrers = (weak_referrer_t *)
calloc(WEAK_INLINE_COUNT, sizeof(weak_referrer_t));
// This constructed table is invalid, but grow_refs_and_insert
// will fix it and rehash it.
for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
new_referrers[i] = entry->inline_referrers[I];
}
entry->referrers = new_referrers;
entry->num_refs = WEAK_INLINE_COUNT;
entry->out_of_line_ness = REFERRERS_OUT_OF_LINE;
entry->mask = WEAK_INLINE_COUNT- 1;
entry->max_hash_displacement = 0;
}
// Additional processing for dynamic arrays:
assert(entry->out_of_line()); // Assertion: dynamic array must be used at this point
if (entry->num_refs >= TABLE_SIZE(entry) * 3/4) { // If the number of elements in the dynamic array is greater than or equal to 3/4 of the total space of the array position, expand the array space by twice the current length
return grow_refs_and_insert(entry, new_referrer); // Expand and insert
}
// If expansion is not required, insert it directly into weak_entry
// Note that weak_entry is a hash table, key: w_hash_pointer(new_referrer) value: new_referrer
// Careful people may notice that the hash algorithm of weak_entry_t is the same as that of weak_table_T, and the algorithm for capacity expansion/reduction is the same
size_t begin = w_hash_pointer(new_referrer) & (entry->mask); // '& (entry->mask)' ensures that the begin position must be greater than or equal to the length of the array
size_t index = begin; // The initial hash index
size_t hash_displacement = 0; // To record the number of hash collisions, that is, the number of times the hash is reshifted
while(entry->referrers[index] ! =nil) {
hash_displacement++;
index = (index+1) & entry->mask; // index + 1, move to the next position, try again to insert. Entry ->mask = 0x111, 0x1111, 0x11111... Because the array grows by *2 each time, that is, 8, 16, 32, corresponding to the dynamic array space length -1 mask, which is the previous value.
if (index == begin) bad_weak_table(entry); // index == begin means that the array is not in a proper position. There must be something wrong.
}
if (hash_displacement > entry->max_hash_displacement) { Max_hash_displacement indicates that if we try max_hash_displacement as many times as possible, we can definitely find the hash position corresponding to the object
entry->max_hash_displacement = hash_displacement;
}
// Store the ref into the hash array and update the element num_refs
weak_referrer_t &ref = entry->referrers[index];
ref = new_referrer;
entry->num_refs++;
}
Copy the code
- First determine whether to use a fixed-length array or a dynamic array
- Fixed-length arrays add the weak pointer address directly to the array
- The fixed – length array is used up. Dump the elements from the fixed – length array into the dynamic array
Weak_unregister_no_lock Removes the old weak pointer address
void
weak_unregister_no_lock(weak_table_t *weak_table, id referent_id,
id *referrer_id)
{
objc_object *referent = (objc_object *)referent_id;
objc_object **referrer = (objc_object **)referrer_id;
weak_entry_t *entry;
if(! referent)return;
if ((entry = weak_entry_for_referent(weak_table, referent))) { // Find the referent corresponding to the weak_entry_t
remove_referrer(entry, referrer); // Remove the referrer from the hash array of the weak_entry_t corresponding to the referent
// After removing the element, check whether the hash array of weak_entry_t is empty
bool empty = true;
if(entry->out_of_line() && entry->num_refs ! =0) {
empty = false;
}
else {
for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
if (entry->inline_referrers[i]) {
empty = false;
break; }}}if (empty) { // If the hash array of weak_ENTRy_t is empty, the weak_ENTRy_t needs to be removed from the Weak_tableweak_entry_remove(weak_table, entry); }}Copy the code
- First, it will find out the weak_entry_t corresponding to the referent in the Weak_table
- Remove the referrer from the weak_entry_t
- After removing the element, judge whether there are still elements in weak_ENTRy_t at this time (empty == true)
- If the weak_ENTRY_T has no elements at this time, it is necessary to remove the weak_ENTRY_T from the Weak_TABLE
Dealloc part
When the reference count of an object is zero, the underlying object is released by calling the _objc_rootDealloc method, and the rootDealloc method is called inside the _objc_rootDealloc method.
inline void
objc_object::rootDealloc()
{
if (isTaggedPointer()) return; // fixme necessary?
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); }}Copy the code
- First, determine whether the object is of type taggedPointer, and if so, return it directly
- If the object is counted in an optimized isa manner, and the object is not weak referenced, has no associated objects, has no custom C++ destructor, and does not use SideTable for reference counting, the object is released quickly
- If 2 is not met, the Dispose method is called
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;
}
Copy the code
If there is a custom C++ destructor, the destructor is called. If there is an associated object, remove the associated object and remove itself from the Association Manager map. Call the clearDeallocation method to clarify the relevant references to the object
inline 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()); }Copy the code
Check whether the object has an optimized ISA reference count. If not, clean up the reference count data stored in the SideTable. If the optimized ISA reference count is used, then determine whether there is an auxiliary reference count using SideTable (ISA.has_SIDEtable_RC) or a weak reference (isA.Weakly_referenced), which conforms to either of these two conditions, Call the clearDeallocating_slow method.
NEVER_INLINE void
objc_object::clearDeallocating_slow()
{
assert(isa.nonpointer && (isa.weakly_referenced || isa.has_sidetable_rc));
SideTable& table = SideTables()[this]; // In the global SideTables, find the corresponding SideTable with this pointer as key
table.lock();
if (isa.weakly_referenced) { // If obj is weakly referenced
weak_clear_no_lock(&table.weak_table, (id)this); // Clean this in the SideTable weak_table
}
if (isa.has_sidetable_rc) { // If SideTable is used for reference counting
table.refcnts.erase(this); // Remove this from the reference count of SideTable
}
table.unlock();
}
Copy the code
Calls the weak_clear_NO_LOCK method to do the cleaning work of weak_table
void
weak_clear_no_lock(weak_table_t *weak_table, id referent_id)
{
objc_object *referent = (objc_object *)referent_id;
weak_entry_t *entry = weak_entry_for_referent(weak_table, referent); // Find the referent corresponding to the weak_entry_t in the weak_table
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;
// Find the weak pointer address array that references the referent and the array length
if (entry->out_of_line()) {
referrers = entry->referrers;
count = TABLE_SIZE(entry);
}
else {
referrers = entry->inline_referrers;
count = WEAK_INLINE_COUNT;
}
for (size_t i = 0; i < count; ++i) {
objc_object **referrer = referrers[i]; // Retrieve the address of each weak PTR
if (referrer) {
if (*referrer == referent) { // If the weak PTR does refer to the referent, then set the weak PTR to nil. That's why the weak pointer is automatically set to nil
*referrer = nil;
}
else if (*referrer) { // If the stored weak PTR does not have a weak reference to the referent, this is probably due to a logic error in the Runtime code
_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();
}
}
}
weak_entry_remove(weak_table, entry); // Since the referent will be released, the referent weak_entry_t should also be removed from the weak_table
}
Copy the code
conclusion
Runtime maintains a weak table. Use to store all weak Pointers to an object. Weak table is actually a hash table of weak_table_T structure. Key is the address of the pointed object, and value is the address array of weak pointer
Weak implementation consists of the following three steps:
- When initialized, it is used by Runtime
objc_initWeak
Function that initializes a new weak pointer to the address of the object - When adding a reference:
objc_initWeak
The function callsobjc_storeWeak()
The function,objc_storeWeak()
Is used to update the pointer to create a weak reference table. - When released, call
clearDeallocating
Function.clearDeallocating
Function firstBy object addressGet all of the weak pointer arrays, then walk through the array, set the data in it to nil, and finally delete the entry from the Weak table, and finally clean up the object’s record.