Memory management portal 🦋🦋🦋
Memory management analysis (a) – MANUAL memory management in the MRC era
Memory management analysis (two) – timer problem
Memory management analysis (three) – iOS program memory layout
Memory management analysis (four) — Autorelease principle analysis
Storage of iOS reference counts
As I explained in my isa insight article, Apple has optimized ISA starting with the ARM64 architecture to store more information in isa Pointers through bit-field counting, making full use of ISA memory space. The current structure of ISA is as follows
union isa_t
{
Class cls;
uintptr_t bits;
struct {
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 deallocating : 1;
uintptr_t has_sidetable_rc : 1;
uintptr_t extra_rc : 19;
};
Copy the code
The EXTRA_rc is used to store reference counts, using the 19 binary values above ISA as storage space. The extra_rc name means the extra reference count, which is the number of times an object has retained at any other time than the retain operation at creation time. So the actual reference count of an object = EXTRA_rc + 1 (the time it was created). Of course, extra_RC is limited in how many expressions it can express. When a reference to an object exceeds the extra_RC representation scope, the has_sideTABLE_rc inside ISA indicates that an object’s reference count cannot be stored in ISA. And stores the reference count value in a property of a class called SideTable. The definition of SideTable can be found in the objC source nsobject.mm file. The following
struct SideTable {
spinlock_t slock;
RefcountMap refcnts;
weak_table_t weak_table;
}
Copy the code
If isa does not store reference counts, the reference counts are stored in the Refcnts of the SideTable, which is actually a hash table structure (similar to a dictionary in OC) as shown by the type RefcountMap.
Weak pointer implementation principle
Across the property, you can set strong, weak, and unsafe_unretained, and translate this to __strong, __weak, and __unsafe_unretained. Let’s take a look at the differences, one by one, through the following code examples and the results
*********************mian.m***************************
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"Temporary scope begins");
{
CLPerson *person = [[CLPerson alloc] init];
NSLog(@"Person object: %@", person);
}
NSLog(@"Temporary scope ends");
NSLog(@"StrongPerson: % @", strongPerson);
}
return 0; } ******************* Result ********************2019- 09. 19:59:00.835983+0800Block learning [24021:2941713The temporary scope begins2019- 09. 19:59:00.836482+0800Block learning [24021:2941713] person object: <CLPerson:0x100704a60>
2019- 09. 19:59:00.836541+0800Block learning [24021:2941713] -[CLPerson dealloc]
2019- 09. 19:59:00.836575+0800Block learning [24021:2941713] Temporary scope ended Program withexit code: 0
Copy the code
The above example makes it clear that the local variable person is released when it is out of temporary scope.
*********************mian.m***************************
int main(int argc, const char * argv[]) {
@autoreleasepool {
__strong CLPerson *strongPerson;
NSLog(@"Temporary scope begins");
{
CLPerson *person = [[CLPerson alloc] init];
NSLog(@"Person object: %@", person);
strongPerson = person;
}
NSLog(@"Temporary scope ends");
NSLog(@"StrongPerson: % @", strongPerson);
}
return 0; } ******************* Result ********************2019- 09. 20:00:07.368972+0800Block learning [24033:2942509The temporary scope begins2019- 09. 20:00:07.369392+0800Block learning [24033:2942509] person object: <CLPerson:0x1007003c0>
2019- 09. 20:00:07.369430+0800Block learning [24033:2942509] the temporary scope ends2019- 09. 20:00:07.369442+0800Block learning [24033:2942509] strongPerson: < CLPerson:0x1007003c0>
2019- 09. 20:00:07.369460+0800Block learning [24033:2942509] -[CLPerson dealloc]
Program ended with exit code: 0
Copy the code
When a person is pointed to by an out-of-scope __strong pointer, you can see that the person object is not destroyed after the temporary scope ends, indicating that the __strong pointer increases the reference count of person
*********************mian.m***************************
int main(int argc, const char * argv[]) {
@autoreleasepool {
__weak CLPerson *weakPerson;
NSLog(@"Temporary scope begins");
{
CLPerson *person = [[CLPerson alloc] init];
NSLog(@"Person object: %@", person);
weakPerson = person;
}
NSLog(@"Temporary scope ends");
NSLog(@"WeakPerson: % @", weakPerson);
}
return 0; } ******************* Result ********************2019- 09 -02 21:48:58.332332+0800 Block learning [24180:2987983The temporary scope begins2019- 09 -02 21:48:58.332851+0800 Block learning [24180:2987983] person object: <CLPerson:0x100600d20>
2019- 09 -02 21:48:58.332889+0800 Block learning [24180:2987983] -[CLPerson dealloc]
2019- 09 -02 21:48:58.332920+0800 Block learning [24180:2987983] the temporary scope ends2019- 09 -02 21:48:58.332938+0800 Block learning [24180:2987983] weakPerson: (null)
Program ended with exit code: 0
Copy the code
When a person is pointed to by an out-of-scope __weak pointer, you can see that when the temporary scope ends, the person is freed as in the first case, indicating that the __weak pointer does not increase the reference count of person, and the __weak pointer is set to nil when the person is freed. Wild pointer errors are prevented
*********************mian.m***************************
int main(int argc, const char * argv[]) {
@autoreleasepool {
__unsafe_unretained CLPerson *unsafePerson;
NSLog(@"Temporary scope begins");
{
CLPerson *person = [[CLPerson alloc] init];
NSLog(@"Person object: %@", person);
unsafePerson = person;
}
NSLog(@"Temporary scope ends");
NSLog(@"UnsafePerson: % @", unsafePerson);
}
return 0; } ******************* Result ********************Copy the code
当person
Being extraterritorial__unsafe_unretained
When the pointer points, you can see that after the temporary scope ends,person
Just like in the first case, it’s just released__unsafe_unretained
The Pointers are not increasingperson
The reference count, however, appears in the endEXC_BAD_ACCESSAn error is reported, indicating a wild pointer problem.
__weak and __unsafe_unretained are different because __weak is automatically set to nil upon release, but __unsafe_unretained is not. So how does Apple automatically clear the __weak pointer after an object is freed? Let’s dig from the source code.
Since the __weak pointer is cleared when the object is released, we start with the object’s dealloc method. We can find the implementation of dealloc in objc source nsobjject. Mm as follows
- (void)dealloc { _objc_rootDealloc(self); } * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *void_objc_rootDealloc(id obj) { assert(obj); obj->rootDealloc(); } * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *inline void objc_object::rootDealloc(a)
{
//🥝 If it is Tagged Pointer, return directly
if (isTaggedPointer()) return; // fixme necessary?
/* 🥝 if (1) the isa is optimized, (2) the weak pointer is not referenced, (3). With no associated objects, no C++ destructors, and no sideTable, you can free() */
if(fastpath(isa.nonpointer && ! isa.weakly_referenced && ! isa.has_assoc && ! isa.has_cxx_dtor && ! isa.has_sidetable_rc)) { assert(! sidetable_present());free(this);
}
else {// If not, we need to use the following function
object_dispose((id)this); }}Copy the code
Enter the object_dispose function
id object_dispose(id obj)
{
if(! obj)return nil;
objc_destructInstance(obj);
free(obj);
returnnil; } * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *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 there is a C++ destructor, call it
if (cxx) object_cxxDestruct(obj);
//🥝 Remove the associated object if it exists
if (assoc) _object_remove_assocations(obj);
// call the following function when 🥝 is done
obj->clearDeallocating();
}
returnobj; } * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *inline void objc_object::clearDeallocating(a)
{
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
In the above method, ifisa
If it is a normal pointer, call it directlysidetable_clearDeallocating
Delta function, if it’s a delta functionoptimizedtheisa
Then goclearDeallocating_slow
Function. Let’s look at these two functions You can see that both functions are internally calledweak_clear_no_lock(&table.weak_table, (id)this);
To deal with__weak
Pointer, where the first argument issideTable
A member of theweak_table
The second argument is the object to be released. Let’s look at the internal logic of the functionThe core approach here isweak_entry_for_referent
Order inObviously, in the above method, through the object to be releasedreferent
An index is obtained according to a certain algorithmindex
And then fromweak_table
Use insideindex
To get the objectreferent
The correspondingweak
The pointer, that showsweak_table
The inside is just a hash table structure that passesobjectAs akey
.value
That’s pointing to thatobjecttheweak
An array of Pointers.
Weak implementation principle summary
- When an object
obj
beweak
When the pointer points to thisweak
A pointer toobj
As akey
Is stored tosideTable
Of the classweak_table
This hash table contains an array of weak Pointers.- When an object
obj
thedealloc
When the method is called, the Runtime returns theobj
forkey
, fromsideTable
theweak_table
In the hash table, find the correspondingweak
Pointer to the list and then place the insideweak
Set the Pointers one by one tonil
.This is how the weak pointer is implemented
Memory management portal 🦋🦋🦋
Memory management analysis (a) – MANUAL memory management in the MRC era
Memory management analysis (two) – timer problem
Memory management analysis (three) – iOS program memory layout
Memory management analysis (four) — Autorelease principle analysis