“This is the 23rd day of my participation in the First Challenge 2022. For details: First Challenge 2022”
Memory management
Swift language continues the same idea of memory management as Objective-C language, which uses reference counting to manage the memory space of instances.
Automatic reference counting
Automatic Reference Counting, also known as ARC, is a means to solve memory management problems in Objective-C and Swift languages. During development, improper reference to class instances will cause memory leaks, which will bring disastrous consequences to applications when accumulated to a certain extent.
Reference counting operations in Swift
We create a Person class like this:
class Person {
var age: Int = 20
var name: String = "zhangSan"
}
Copy the code
Next, let’s print and parse the memory pointer to the instance of Person:
As we already know from our previous analysis, the second 8-byte store is refCounts, which is 0x3; In the heapObject. h file, we can find the definition of refCounts in HeapObject:
RefCounts is a template class that accepts the InlineRefCountBits generic argument:
typedef RefCounts<InlineRefCountBits> InlineRefCounts;
Copy the code
RefCounts essentially operate on the generic parameter we passed, RefCountsBits, which is a wrapper around the reference count, depending on the parameter type InlineRefCountBits;
Let’s continue with the definition of InlineRefCountBits:
typedef RefCountBitsT<RefCountIsInline> InlineRefCountBits;
Copy the code
This is also a template function, and RefCountIsInline is true:
enum RefCountInlinedness { RefCountNotInline = false.RefCountIsInline = true };
Copy the code
So, we know that the class for the final reference-counting operation is actually RefCountBitsT:
When we analyze RefCountBitsT, we find that there is only one bits property, which is defined by the Type property of RefCountBitsInt; According to the definition of Type, we find that Type is actually a uint64_t Type, which is a 64-bit bit field information:
typedef uint64_t Type;
Copy the code
So far, we can draw a conclusion:
Reference counting is essentially a 64-bit bit field information in which reference counting information is stored. The reason for doing this is to abstract it out and improve code reuse;
So, the question is, when we create a new instance object, what is its reference count? Initialize HeapObject ();
From the HeapObject initialization implementation, we find the refCounts assignment:
Initialized is an enumerated type Initialized_t: Initialized_t: Initialized_t: Initialized_t: Initialized_t
RefCountBits(0,1); RefCountBits(RefCountBitsT); RefCountBitsT (RefCountBitsT); RefCountBitsT (RefCountBitsT);
StrongExtraCount is 0 and unownedCount is 1. StrongExtraCount (strong reference count) and unownedCount(no primary reference count) are stored in 64 bits by displacement. In the source code we can analyze as follows:
# define shiftAfterField(name) (name##Shift + name##BitCount)
static const size_t PureSwiftDeallocShift = 0;
static const size_t PureSwiftDeallocBitCount = 1;
static const size_t StrongExtraRefCountShift = shiftAfterField(IsDeiniting);
static const size_t PureSwiftDeallocShift = 0;
static const size_t UnownedRefCountShift = shiftAfterField(PureSwiftDealloc);
static const size_t IsDeinitingShift = shiftAfterField(UnownedRefCount);
static const size_t IsDeinitingBitCount = 1;
static const size_t UnownedRefCountShift = shiftAfterField(PureSwiftDealloc);
static const size_t UnownedRefCountBitCount = 31;
// Result analysis
StrongExtraRefCountShift = shiftAfterField(IsDeiniting)
= IsDeinitingShift + IsDeinitingBitCount
= shiftAfterField(UnownedRefCount) + 1
= UnownedRefCountShift + UnownedRefCountBitCount + 1
= shiftAfterField(PureSwiftDealloc) + 31 + 1
= PureSwiftDeallocShift + PureSwiftDeallocBitCount + 31 + 1
= 0 + 1 + 31 + 1 = 33
Copy the code
Through the above calculation, we can analyze as follows:
bits( (0 << 33) | (1 << 0) | (1 << 1))
bits( 0 | 1 | 2)
bit(3)
Matches our final print of 0x3;
64-bit stored information is as follows:
Bit 0: identifies whether it is permanent bit 1-31: stores no primary references Bit 32: identifies whether the current class is being destructed Bit 33-62: identifies strong references bit 63: identifies whether SlowRC is used