IOS analysis of the realization of the basic principles of weak (including the initialization of the weak object, reference, release analysis)

Few people know that the weak table is actually a hash (Hashi) table, Key is the address of the object referred to, Value is the weak pointer address array. More people just know that weak is a weak reference, the reference object will not add a counter, and when the reference object is released automatically set to nil. Usually used to solve circular reference problems. But now only know that these are not enough to deal with the interview, many companies will ask the principle of weak. What is the principle of weak? Here on the analysis of the working principle of weak (just curious about this problem, the process of learning notes, I hope to help the reader).

Generalization of weak implementation principle

Runtime maintains a weak table that stores all weak pointers to an object. The weak table is actually a hash (Hashi) table, Key is the address of the object, Value is the address of the weak pointer (the value of the address is the address of the object pointer) array.

Weak principle can be summarized in three steps:

1, initialization: runtime will call the objc_initWeak function to initialize a new weak pointer to the address of the object.
2, add reference: objc_initWeak function will call objc_storeWeak () function, objc_storeWeak () function is to update the pointer to the point, to create a corresponding weak reference table.
3, release, call the clearDeallocating function. According to the clearDeallocating function of the first array object address access all weak pointer address, then the data traversing the array is set to nil, finally remove the entry from the weak table, finally clean up the object record.

The following will start detailing each step:

1, initialization: runtime will call the objc_initWeak function, the objc_initWeak function will initialize a new weak pointer to the address of the object.

Sample code:

{*obj = [[NSObject alloc] init]; ID obj1 = obj;}; __weak = NSObject

When we initialize a weak variable, runtime calls the objc_initWeak function in NSObject.mm. The declaration of this function in Clang is as follows:

ID objc_initWeak (ID *object, ID value);

For the realization of objc_initWeak () method

ID objc_initWeak (ID *location, ID newObj) {/ / view object instance is valid / invalid object pointer directly leads to the release of if (! NewObj) {*location = nil; return nil;} / / this transmission of three bool / template for the use of numerical constants for the parameter optimization of the performance of return storeWeakfalse/*old*/, true/*new*/, true/*crash*/> (location, newObj (objc_object*));}

We can see that this function is only a function call and deep entrance, entrance function in general, will do some simple judgment (for example in the objc_msgSend cache, the judge judge) here the class object pointer is valid, invalid released directly to the deep, no longer call function. Otherwise, object will be registered as a __weak object to value. And this should be the objc_storeWeak function.

Note: the objc_initWeak function has a premise that object must be a valid pointer to a __weak object that has not been registered. Value can be null, or point to a valid object.

2, add reference: objc_initWeak function will call objc_storeWeak () function, objc_storeWeak () function is to update the pointer to the point, to create a corresponding weak reference table.

The function of objc_storeWeak is declared as follows:

ID objc_storeWeak (ID *location, ID value);

Objc_storeWeak () specific implementation is as follows:

True / / HaveOld: / / false - - variable values need to be cleared up, the current value may be nil / / HaveNew: true - new value is assigned, the value may be nil / / false - do not need to assign a new value of true - newObj / / CrashIfDeallocating: has been released or newObj does not support the weak reference, the process needs to pause / / false - nil template bool HaveOld bool alternative storage, HaveNew, bool CrashIfDeallocating> static ID storeWeak (ID *location, objc_object *newObj) {/ / this process is used to update the weak reference pointer pointing / / initialized pointer Class previouslyInitializedClass previouslyInitializedClass = nil; ID oldObj; / / SideTable / / 1 statement two new hash to create SideTable *oldTab Le; SideTable *newTable; / / get the new value and the old value of the latch position (with the address as the only sign) to establish the index marks through the address / /, / / the following points to avoid tub repeat operation will change the old value of retry: if (HaveOld) {/ / change pointer, oldObj index to the stored value. OldObj = *location; oldTable = & SideTables ([oldObj]);} else {oldTable = nil;} if (HaveNew) {/ / change the new pointer, to obtain the newObj index value of the stored address newTable = & SideTables ([newObj]);} else {newTable = nil;} / / lock operation. To prevent the conflict SideTable: competition in multi thread: lockTwoHaveOld, HaveNew> (oldTable, newTable); / / avoid line Process conflict processing / / location should be consistent with oldObj, if different, indicating that the current location has dealt with oldObj but also by other threads that modify if (HaveOld & & *location! = oldObj) {SideTable:: unlockTwoHaveOld, HaveNew> (oldTable, newTable); goto retry;} / / prevent weak references between deadlock / / and through the +initialize initializer to ensure that all non empty weak references to if ISA (HaveNew & & newObj) {/ / new isa pointer Class CLS = newObj-> getIsa (ISA); / / judgment is nonempty and initialized if (CLS! = previouslyInitializedClass & & objc_class ((! * -> isInitialized (CLS)))) {/ / unlock SideTable:: unlockTwoHaveOld, HaveNew> (oldTable, newTable); / / initialize the _class_initialize isa pointer (_class_getNonMetaClass (CLS (ID) newObj)); / / if the class has completed execution +initialize method is the most ideal / / if the class +initialize in the thread / / such as +initialize are calling the storeWeak method on the need to manually / / the increased protection strategy, and set the previouslyInitializedClass pointer labeled previouslyInitializedClass = CLS; / / goto retry}} try again; / / remove the old value if (HaveOld) {weak_unregister_no_lock (& oldTable-> weak_table, oldObj, location);} / / 3 Assign a new value of if (HaveNew) {newObj = (objc_object * weak_register_no_lock) (& newTable-> weak_table (ID), newObj, location, CrashIfDeallocating); / / if a weak reference is released the weak_register_no_lock method returns nil / / in the reference count table set up if the reference mark if (newObj & & newObj-> isTaggedPointer! ()) {/ / / / initialization operation refers to a weak reference counting the hash table weak reference object identified as weak reference in newObj-> setWeaklyReferenced_nolock (); count;} / / before do not set the location object here need to change the pointer *location = newObj (ID);} else {/ / no new value, there is no need to change: unlockTwoHaveOld, HaveNew>} SideTable: (oldTable, newTable); return (ID newObj);}

In addition to the source of a variety of lock operations, to see what this code has done.

1), SideTable

SideTable this structure, I named the reference count and the weak reference dependency table, because it is mainly used to manage the object’s reference count and weak table. Declare its data structure in NSObject.mm:

Struct SideTable {/ / ensure spin atomic operation lock spinlock_t slock hash RefcountMap refcnts; / / reference table counting; / / weak hash weak_table_t weak_table global reference table;

For the two members of the slock
} and refcnts needless to say, the first is the choice of competition in order to prevent the spin lock, the second is to assist the ISA pointer extra_rc common reference count variables (for the object, referred to in the future in this paper). Here mainly to see the structure and role of the global hash weak table.

2), weak table

The weak table is a weak reference table, implemented as a weak_table_t structure that stores all of the weak reference information associated with an object. The definition is as follows (defined in objc-weak.h):

Struct weak_table_t {/ / save all point to the specified object pointer to weak weak_entry_t *weak_entries; / / num_entries / / size_t of storage space; in the judgment reference counting assistant quantity uintptr_t mask; / / hash key maximum deviation value uintptr_t max_hash_displacement;};

This is a global weak reference hash table. Use the address of an indeterminate type object as key, use the weak_entry_t type structure object as value. One of the weak_entries members, from the literal point of view, is a weak reference table entry. In fact, this is also the case.

Where weak_entry_t is an internal structure stored in a weak reference table, it is responsible for maintaining and storing all weak references to an object hash table. The definition is as follows:

Typedef objc_object * * weak_referrer_t; struct weak_entry_t {DisguisedPtrobjc_object> referent; union {struct {weak_referrer_t *referrers uintptr_t; out_of_line: 1; uintptr_t: PTR_MINUS_1; num_refs uintptr_t mask; uintptr_t max_hash_displacement;}; struct out_of_line=0 is LSB of {/ / one of these (don't care which) weak_referrer_t inline_referrers[WEAK_INLINE_COUNT];};}}

In the weak_entry_t structure, DisguisedPtr referent is a generic object pointer to do a package, through this generic class to solve the problem of memory leaks. Write the out_of_line member from the comment as the least significant bit, when it is 0, the weak_referrer_t member will be extended to multi row static hash table. In fact, the weak_referrer_t is the name of the two-dimensional objc_object, through a two-dimensional pointer address offset, with subscript key as hash, made a weak reference hash.
so what’s the effect of out_of_line, num_refs, mask, and max_hash_displacement when the bit is not working? The following is my own guess:

Out_of_line: the least significant bit, and the flag bit. When the flag bit 0, increase the reference table pointer latitude.
num_refs: reference value. A weak reference table is referenced in the reference to a valid number, because the weak reference table uses a static hash structure, so a variable is used to record the number.
mask: count assist.
max_hash_displacement:hash element upper threshold. In fact, the value of out_of_line is usually equal to zero, so the weak reference table is always a two-dimensional array of objc_objective pointers (
). One dimensional objc_objective pointer can form a weak reference list, through third latitude to achieve a number of hash table, and the number of tables WEAK_INLINE_COUNT.

Summary StripedMap[]:StripedMap is a template class that has a array member in this class, used to store PaddedT objects, and the character of the overloaded definition [], will return to the PaddedT member of the value, the value is our incoming T generic member, which is the SideTable object. In the array subscript, where the use of the indexForPointer method to calculate the subscript through bit operations, to achieve a static Hash Table. In weak_table, its member weak_entry encapsulates the address of the incoming object, and it also has access to the global weak reference table.

IOS analysis of the realization of the basic principles of weak (including the initialization of the weak object, reference, release analysis)
31B622B5-77ED-4D50-8CF9-0803785117BC.png

Old object to cancel the registration operation weak_unregister_no_lock

The main function of this method is to contact the weak object in the weak_table pointer. According to the name of the function, called the release of the registration operation. From the source code, you can know that its function is to contact the weak pointer from the weak_table binding. And the traversal query, which is aimed at the weak_entry in the multi weak reference scattered list.

Add registration operation to new object weak_register_no_lock

This step is contrary to the previous step, through the weak_register_no_lock function to register the heart of the object operation, complete with the corresponding weak reference table for binding operations.

Initial weak reference object flow

Weak reference initialization, as can be seen from the above analysis, the main part of the operation in the weak reference table keys, query hash, create a weak reference table and other operations, you can summarize the following flow chart:

IOS analysis of the realization of the basic principles of weak (including the initialization of the weak object, reference, release analysis)
C6029C92-5145-4334-9C25-3CDBA50F142B.png

There are a lot of cases in the
, but when you declare a weak, you call the methods in the figure above. Of course, the storeWeak method is not only used in the weak statement, the class internal operations will often be used to operate the weak object.

3, release, call the clearDeallocating function. According to the clearDeallocating function of the first array object address access all weak pointer address, then the data traversing the array is set to nil, finally remove the entry from the weak table, finally clean up the object record.

When the weak reference object is released, how to deal with the weak pointer? When an object is released, the basic flow is as follows:

1, call the objc_release
2, because the object’s reference count is 0, so the implementation of dealloc
3, in dealloc, call the _objc_rootDealloc function
, 4 in _objc_rootDealloc, the call to object_dispose
5,
6, last call objc_destructInstance call objc_clear_deallocating

Focus on the objc_clear_deallocating function called when the object is released. The function is implemented as follows:

Void objc_clear_deallocating (ID obj) {assert (obj); assert ((UseGC)); if (obj-> isTaggedPointer) (return); obj-> (clearDeallocating);}

It is called the clearDeallocating, continue to track can be found, it is used to select weak value iterator, then call weak_clear_no_lock, then find the corresponding value, the weak pointer to null, weak_clear_no_lock function to achieve the following:

By dealloc Nils / * * * Called out all weak pointers; that point to the object so that they * provided can no longer be used. @param weak_table * * * @param referent The object being deallocated. / void weak_clear_no_lock (weak_table_t *weak_table, ID referent_id) {objc_object *referent = (objc_object * referent_id); weak_entry_t (weak_table = weak_entry_for_referent, *entry 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; coun T = 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]; if (referrer) {if (*referrer = = referent) {*referrer = nil; if (*referrer) {else} _objc_inform ("__weak variable at%p holds%p instead of%p." This "is probably incorrect" objc_storeWeak use of "(and) (objc_loadWeak)." "Break on objc_weak_error to debug./n, referrer, *referrer (void*), referent objc_wea (void*)); K_error ();}} weak_entry_remove (weak_table, entry);}

Objc_clear_deallocating the function is as follows:

1, from the weak table to obtain the address of discarded objects to record the
key 2, will contain all the weak modifier with the address of the variable in the record, the assignment is nil
3, the weak table in the
4, to delete the record from the reference count table delete discarded objects address key records

Read the objc-weak.mm source code to understand: in fact, the Weak table is a hash (Hashi) table, and then the inside of the key is pointing to the object’s address, Value is the Weak pointer to the address of the array.

Added:.M and.Mm difference

.m: source code file, the typical source code file extension, you can include OC and C code.
.Mm: source code file, with the extension of the source code file, in addition to OC and C code can be included, but also can contain C++ code. Use this extension only if you really need to use the C++ class or features in your OC code.

Reference:
weak weak reference to achieve the life cycle of
weak: specific implementation methods