Memory layout
- The stack area【 Usually starts with 0x7 】
- function
- Pointer to the
- Local variables, method parameters
- The heap area【 Usually starts with 0x6 】
- An object that allocates memory by alloc
- block copy
- Uninitialized data.bss【 Usually starts with 0x1 】
- An uninitialized global variable
- An uninitialized static variable
- Data. The data【 Usually starts with 0x1 】
- Initialized global variable
- Initialized static variable
- Code. The text
- Program code, loaded into memory
Why is stack access faster than heap access? Because the memory in the stack is located through the SP register, and the heap address needs to be located through the register to the address containing the memory space, and then through this address to the specific location.
Memory management scheme
TaggedPointer
basis
- Dedicated to storing small objects (
NSNumber
.NSDate
…). - The value of a pointer is no longer an address, but a real value. So, really, it’s just a normal variable in object skin. So, its memoryIt’s not stored in the heapI don’t need to
malloc
andfree
- Three times more efficient at memory reads and 106 times faster at creation
NONPOINTER_ISA
basis
- Non-pointer ISA
- We are exploring
isa_t
You will see that one is stored internallyISA_BITFIELD
Take a look at the arm64 architectureISA_BITFIELD
#define ISA_BITFIELD
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t has_cxx_dtor : 1; \
uintptr_t shiftcls : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; \
uintptr_t unused : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 19
Copy the code
nonpointer
: Indicates whether to enable pointer optimization for isa Pointers. 0: pure ISA pointer, 1: not only class object address, ISA contains class information, object reference count.has_assoc
: Indicates the flag of the associated object. 0: no, 1: existhas_cxx_dtor
Does the object have a destructor for C++/Objc? If it has a destructor, then the destructor logic needs to be done. If not, the object can be freed fastershiftcls
: Stores the value of the class pointer. With pointer optimization turned on, 33 bits are used to store class Pointers in the ARM64 architecture.magic
: used by the debugger to determine whether the current object is a real object or has no space to initializeweak_referenced
: indicates whether an object is or has been referred to an ARC weak variable. Objects without weak references can be released fasterhas_sidetable_rc
: When the reference count of an object is greater than 10, the variable is borrowed to store the carryextra_rc
For example, if the reference count of the object is 10, thenextra_rc
Is 9. If the reference count is greater than 10, use the one abovehas_sidetable_rc
: When the reference count of an object is greater than 10, you need to borrow this variable to store carry
Hash table SideTable
basis
Hash table hash table hash table hash table hash table hash table hash table hash table hash table
class StripedMap {
#ifTARGET_OS_IPHONE && ! TARGET_OS_SIMULATOR
enum { StripeCount = 8 };
#else
enum { StripeCount = 64 };
#endif...}Copy the code
According to the StripedMap definition, there are 8 Sidetables on iPhone and 64 sidetables on other platforms
struct SideTable {
spinlock_t slock;
RefcountMap refcnts; // Reference count
weak_table_t weak_table; // Weak reference table
}
// weak_table_t
struct weak_table_t {
weak_entry_t *weak_entries;
size_t num_entries;
uintptr_t mask;
uintptr_t max_hash_displacement;
};
Copy the code
MRC & ARC
Retain Process Exploration
When we create an object, we can see from the breakpoint that we go to the _objc_rootRetain method
// _objc_rootRetain
id _objc_rootRetain(id obj) {
ASSERT(obj);
return obj->rootRetain(a); }// rootRetain
id objc_object::rootRetain(a) {
return rootRetain(false, RRVariant::Fast);
}
// Call the overloaded rootRetain method with false and Fast
id objc_object::rootRetain(bool tryRetain, objc_object::RRVariant variant) {···}
Copy the code
- Whether it is
TaggedPointer
If so, return itself directly (no reference counting), if not, continue.
if (slowpath(isTaggedPointer())) return (id)this;
Copy the code
- Whether it is
nonpointer
Type, if isnonpointer
- Determines whether a retain operation is undergoing demolition or not
extra_rc
If the value + 1carry
representsnewisa.bits
Overflow occurs when storage is full
/ / operation
uintptr_t carry; // carry indicates whether overflow occurs
newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry); // extra_rc++
// RC_ONE
# define RC_ONE (1ULL<<56)
Copy the code
If it overflows and the passed argument is not RRVariant::Full, go to the rootRetain_overflow method and return.
if(variant ! = RRVariant::Full) {ClearExclusive(&isa.bits);
return rootRetain_overflow(tryRetain);
}
// You can see that rootRetain is still called internally, but passed in as RRVariant::Full
id objc_object::rootRetain_overflow(bool tryRetain) {
return rootRetain(tryRetain, RRVariant::Full);
}
Copy the code
If the parameter is not RRVariant::Full, only half of newisa.extra_rc is retained
// Set the hash table flag to true
sideTableLocked = true;
transcribeToSideTable = true;
// The extra_rc is left in half and has_sideTABLE_rc is set to true for that object
newisa.extra_rc = RC_HALF; // # define RC_HALF (1ULL<<18)
newisa.has_sidetable_rc = true;
Copy the code
- If not for
nonpointer
Type, go tosidetable_retain
return sidetable_retain(sideTableLocked) // false
Copy the code
I drew a simple flow chart
weak
When we decorate a variable with __weak, the breakpoint run will see the objc_initWeak method arrive.
id objc_initWeak(id *location, id newObj) {
if(! newObj) { *location = nil;return nil;
}
return storeWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating>
(location, (objc_object*)newObj);
}
Copy the code
Let’s explore the storeWeak method.
- According to incoming
newObj
Get hash table
newTable = &SideTables()[newObj];
Copy the code
2. Create a weak reference table
weak_register_no_lock(&newTable->weak_table,
(id)newObj,
location,
crashIfDeallocating ? CrashIfDeallocating : ReturnNilIfDeallocating);
Copy the code
Focus on the following lines in the Weak_register_NO_lock method
// Address of the weakly referenced object
objc_object *referent = (objc_object *)referent_id;
// Address of the weakly referenced object pointer
objc_object **referrer = (objc_object **)referrer_id;
/ / create weak_entry_t
weak_entry_t new_entry(referent, referrer);
// Determine whether capacity expansion is required
weak_grow_maybe(weak_table);
// Add the new Weak_entry_t to the weak reference table
weak_entry_insert(weak_table, &new_entry);
Copy the code
Weak_entry_t structure
struct weak_entry_t {
DisguisedPtr<objc_object> referent;
union {...// Since it is the union bitfield structure, we only focus on the following part for the moment
struct {
weak_referrer_t inline_referrers[WEAK_INLINE_COUNT];
};
};
// Initialize method
weak_entry_t(objc_object *newReferent, objc_object **newReferrer)
: referent(newReferent)
{
inline_referrers[0] = newReferrer;
for (int i = 1; i < WEAK_INLINE_COUNT; i++) { / / WEAK_INLINE_COUNT of 4inline_referrers[i] = nil; }}}Copy the code