Swift pointer
Swift
There are two types of Pointers intyped pointer
Specify a pointer to the data type,raw pointer
No specified data type pointer (native pointer)raw pointer
inSwift
Is represented byUnsafeRawPointer
tyepd pointer
inSwift
Is represented byUnsafePointer<T>
Swift
The pointer compares the OC pointer
Swift | oc | instructions |
---|---|---|
UnsafePointer | const T * | Pointers and what they point to are immutable |
UnsafeMutablePointer | T * | Pointers and what they point to are mutable |
UnsafeRawPointer | const void * | The pointer and what it points to are immutable and the pointer points to an unknown type |
UnsafeMutableRawPointer | void * | Pointer and what it points to are mutable and pointer to unknown type |
The original pointer
- We want to store four consecutive integers in memory, via native Pointers
raw pointer
How to implement
/ / 1, 32 bytes allocated memory size let p = UnsafeMutableRawPointer. The allocate (byteCount: 32, alignment: Stride // storeBytes: This is to store our data. Stride // For I in 0... <4 { p.advanced(by: i * 8).storeBytes(of: (i + 1), as: Int. Self)} // 3, load: fromByteOffset: for I in 0.. <4 { let value = p.load(fromByteOffset: i * 8, as: Int.self) print("index:\(i)--value:\(value)") } p.deallocate()Copy the code
Specifies a data type pointer
- Gets the address of a variable
Var age = 10 // Let p = withUnsafePointer(to: &age, {$0}) // withUnsafePointer(to: Age, {$0}) print(p.pointee) withUnsafePointer(to: &age, {PTR in ptr.pointee + 12}) print(b) // If we want to change pointer. pointee, WithUnsafeMutablePointer withUnsafeMutablePointer(to: &age, {PTR in PTR. Pointee += 12 print(PTR. Pointee)}) withUnsafeMutablePointer withUnsafeMutablePointer(to: &age, {PTR in PTR. Pointee += 12 print(PTR. Pointee)})Copy the code
- Another creation
Type Pointer
The way of
Struct XQTeacher {var age = 18 var height = 1.88} MemoryLayout<XQTeacher>. Stride let p = UnsafeMutablePointer<XQTeacher>. Allocate (capacity: 1) // Initialize the current UnsafeMutablePointer<XQTeacher> pointer P.itialize (to: XQTeacher()) p.advanced(by: 1). Initialize (to: Print (p[0]) print(p[1]) print(p[0]) print(p[1]) print(p[1]) print(p[1]) print(p[1])Copy the code
Use examples to understand Pointers
- 1. Bind to swift_class by pointer
struct HeapObject { var kind : UnsafeRawPointer var strongRef : UInt32 var unownedRef : UInt32 } struct xq_swift_class { var kind : UnsafeRawPointer var superClass : UnsafeRawPointer var cachedata1 : UnsafeRawPointer var cachedata2 : UnsafeRawPointer var data : UnsafeRawPointer var flags : UInt32 var instanceAddressOffset : UInt32 var instanceSize : UInt32 var instanceAlignMask : UInt16 var reserved : UInt16 var classSize : UInt32 var classAddressOffset : UInt32 var description : UnsafeRawPointer }; Class XQTeacher 18} {var age = / / the memory address of the instance variable var t = XQTeacher () / / Unmanagedpass Unretained (t as AnyObject) toOpaque () // How OC and CF interact, __brige, Across (t as AnyObject). ToOpaque () let heapObject = ptr.bindMemory(to: HeapObject.self, capacity: 1) let metaPtr = heapObject.pointee.kind.bindMemory(to: xq_swift_class.self, capacity: Xq_swift_class (kind: 0x0000000100008140, superClass: 0x0000000100008140) print(metaptr. pointee) 0x00007fff889728f8, cachedata1: 0x00007fff201f0af0, cachedata2: 0x0000802000000000, data: 0x0000000100606fe2, flags: 2, instanceAddressOffset: 0, instanceSize: 24, instanceAlignMask: 7, reserved: 0, classSize: 136, classAddressOffset: 16, description: 0x0000000100003c3c)Copy the code
- 2. Tuple pointer type conversion
Var tul = (10,20) withUnsafePointer(to: &tul, {(PTR: UnsafePointer<(Int,Int)>) in testPointer(UnsafeRawPointer(ptr).assumingMemoryBound(to: Int.self)) }) func testPointer(_ p : UnsafePointer<Int>) { print(p) print("end") }Copy the code
- 3. Get a pointer to the structure’s properties
struct HeapObject { var strongRef = 10 var unownedRef = 20 } var h = HeapObject() withUnsafePointer(to: &h, { (ptr : // Let strongRefPtr = UnsafeRawPointer(PTR) + MemoryLayout<HeapObject>.offset(of:\HeapObject.strongRef)! testPointer(strongRefPtr.assumingMemoryBound(to: Int.self)) }) func testPointer(_ p : UnsafePointer<Int>) { print(p) print("end") }Copy the code
withMemoryRebound
: Temporarily changes the memory binding type. Usage scenario: The parameter of a function call needs to be passed oneUnsafePointer<UInt64>
The type argument, the age that we pass in is actually oneUnsafePointer<Int>
You can change the binding type temporarily without changing its type, only in the scope of the closure
var age = 10
withUnsafePointer(to: &age, { (ptr : UnsafePointer<Int>) in
ptr.withMemoryRebound(to: UInt64.self, capacity: 1, { ( ptr : UnsafePointer<UInt64> ) in
testPointer(ptr)
})
})
func testPointer(_ p : UnsafePointer<UInt64>) {
print(p.pointee)
print("end")
}
Copy the code
bindMemory(to: capacity:)
: Changes the memory binding type, if no previous binding, then it is the first binding; If it does, it will be rebound to that type.assumingMemoryBound
Assuming memory binding, this tells the compiler: I am this type, don’t check me any more.
Swift Memory Management
Swift
To manage memory using an automatic reference counting (ARC) mechanism, we create a class to observe its reference reference count
class XQTeacher {
var age = 18
var name = "xq"
}
var t = XQTeacher()
var t1 = t
var t2 = t
Copy the code
- through
lldb
Debug prints its reference count
Source code analysis reference count
- We analyze it by reading the source code directly
refCounted
We knowHeapObject
There are two properties in thereHeapMetadata const *metadata;
andInlineRefCounts refCounts
refCounts
The reference counting properties,InlineRefCounts
It’s an alias
typedef RefCounts<InlineRefCountBits> InlineRefCounts;
Copy the code
RefCounts
Is a template class whose structure is determined by the parameters passed in
InlineRefCountBits
Another alias
typedef RefCountBitsT<RefCountIsInline> InlineRefCountBits;
Copy the code
RefCountIsInline
Is aenum
enum RefCountInlinedness { RefCountNotInline = false, RefCountIsInline = true };
Copy the code
RefCountBitsT
Structure, which has one parameterBitsType bits
.BitsType
isRefCountBitsInt<refcountIsInline, sizeof(void*)>::Type
The alias
RefCountBitsInt
Bits is actually a uint64_t type of data
struct RefCountBitsInt<RefCountNotInline, 4> {
typedef uint64_t Type;
typedef int64_t SignedType;
};
Copy the code
- When I go back to class initialization,
enum Initialized_t { Initialized };
The initialization
Offsets
The definition of
struct RefCountBitOffsets<8> {
/*
The bottom 32 bits (on 64 bit architectures, fewer on 32 bit) of the refcount
field are effectively a union of two different configurations:
---Normal case---
Bit 0: Does this object need to call out to the ObjC runtime for deallocation
Bits 1-31: Unowned refcount
---Immortal case---
All bits set, the object does not deallocate or have a refcount
*/
static const size_t PureSwiftDeallocShift = 0;
static const size_t PureSwiftDeallocBitCount = 1;
static const uint64_t PureSwiftDeallocMask = maskForField(PureSwiftDealloc);
static const size_t UnownedRefCountShift = shiftAfterField(PureSwiftDealloc);
static const size_t UnownedRefCountBitCount = 31;
static const uint64_t UnownedRefCountMask = maskForField(UnownedRefCount);
static const size_t IsImmortalShift = 0; // overlaps PureSwiftDealloc and UnownedRefCount
static const size_t IsImmortalBitCount = 32;
static const uint64_t IsImmortalMask = maskForField(IsImmortal);
static const size_t IsDeinitingShift = shiftAfterField(UnownedRefCount);
static const size_t IsDeinitingBitCount = 1;
static const uint64_t IsDeinitingMask = maskForField(IsDeiniting);
static const size_t StrongExtraRefCountShift = shiftAfterField(IsDeiniting);
static const size_t StrongExtraRefCountBitCount = 30;
static const uint64_t StrongExtraRefCountMask = maskForField(StrongExtraRefCount);
static const size_t UseSlowRCShift = shiftAfterField(StrongExtraRefCount);
static const size_t UseSlowRCBitCount = 1;
static const uint64_t UseSlowRCMask = maskForField(UseSlowRC);
static const size_t SideTableShift = 0;
static const size_t SideTableBitCount = 62;
static const uint64_t SideTableMask = maskForField(SideTable);
static const size_t SideTableUnusedLowBits = 3;
static const size_t SideTableMarkShift = SideTableBitCount;
static const size_t SideTableMarkBitCount = 1;
static const uint64_t SideTableMarkMask = maskForField(SideTableMark);
};
Copy the code
- Finally, we come to the following conclusion
swift_retain
- Search in the source code
swift_retain
To findswift_retain
You can see that it is executedobject->refCounts.increment(1);
function
increment
The implementation of theincrementStrongExtraRefCount
function
void increment(uint32_t inc = 1) { auto oldbits = refCounts.load(SWIFT_MEMORY_ORDER_CONSUME); // constant propagation will remove this in swift_retain, it should only // be present in swift_retain_n if (inc ! = 1 && oldbits.isImmortal(true)) { return; } RefCountBits newbits; do { newbits = oldbits; bool fast = newbits.incrementStrongExtraRefCount(inc); if (SWIFT_UNLIKELY(! fast)) { if (oldbits.isImmortal(false)) return; return incrementSlow(oldbits, inc); } } while (! refCounts.compare_exchange_weak(oldbits, newbits, std::memory_order_relaxed)); }Copy the code
- function
incrementStrongExtraRefCount
, will pass the parameter into strongBitsType
Through the above study, we know that it is actuallyuint64_t
Type, and then the displacement operationStrongExtraRefCountShift
.
// Returns true if the increment is a fast-path result.
// Returns false if the increment should fall back to some slow path
// (for example, because UseSlowRC is set or because the refcount overflowed).
LLVM_NODISCARD LLVM_ATTRIBUTE_ALWAYS_INLINE
bool incrementStrongExtraRefCount(uint32_t inc) {
// This deliberately overflows into the UseSlowRC field.
bits += BitsType(inc) << Offsets::StrongExtraRefCountShift;
return (SignedBitsType(bits) >= 0);
}
Copy the code
A weak reference
- A weak reference declares a variable
weak var t = XQTeacher()
, you can see that it is an optional value because the program is allowed to set the current variable to nil during runtime.
- You can see if you don’t use
weak
Modifier is not allowed to be set to nil - Using the
weak
And then after that, let’s look at itrefcount
As you can seerefcount
Changed.
- So let’s look at that
weak
What did you do in theweak
To set a breakpoint, run into the breakpoint
- We have studied it above
swift_retain
.swift_release
andswift_retain
It’s corresponding. We focus on itswift_weakInit
WeakReference *swift::swift_weakInit(WeakReference *ref, HeapObject *value) { ref->nativeInit(value); return ref; } 👇 void nativeInit(HeapObject *object) {auto side = object? object->refCounts.formWeakReference() : nullptr; nativeValue.store(WeakReferenceBits(side), std::memory_order_relaxed); } 👇 // SideTableRefCountBits Busbusallowance does not exist. Template <> HeapObjectSideTableEntry* RefCounts<InlineRefCountBits>::formWeakReference() { auto side = allocateSideTable(true); if (side) return side->incrementWeak(); else return nullptr; } 👇 template <> HeapObjectSideTableEntry* RefCounts<InlineRefCountBits>::allocateSideTable(bool failIfDeiniting) {auto oldbits = refCounts.load(SWIFT_MEMORY_ORDER_CONSUME); // Preflight failures before allocating a new side table. if (oldbits.hasSideTable()) { // Already have a side table. Return it. return oldbits.getSideTable(); } else if (failIfDeiniting && oldbits.getIsDeiniting()) { // Already past the start of deinit. Do nothing. return nullptr; } // Preflight passed. Allocate a side table. // FIXME: custom side table allocator HeapObjectSideTableEntry *side = new HeapObjectSideTableEntry(getHeapObject()); auto newbits = InlineRefCountBits(side); do { if (oldbits.hasSideTable()) { // Already have a side table. Return it and delete ours. // Read before delete to streamline barriers. auto result = oldbits.getSideTable(); delete side; return result; } else if (failIfDeiniting && oldbits.getIsDeiniting()) { // Already past the start of deinit. Do nothing. return nullptr; } side->initRefCounts(oldbits); } while (! refCounts.compare_exchange_weak(oldbits, newbits, std::memory_order_release, std::memory_order_relaxed)); return side; }Copy the code
HeapObjectSideTableEntry
There are two properties in the classobject
andrefCounts
SideTableRefCounts
The definition of
- The structure of SideTableRefCountBits
auto newbits = InlineRefCountBits(side);
Function calls the following initialization function.
LLVM_ATTRIBUTE_ALWAYS_INLINE RefCountBitsT(HeapObjectSideTableEntry* side) : Bits ((reinterpret_cast < BitsType > (side) > > Offsets: : SideTableUnusedLowBits) / / moves to the right three | (BitsType (1) < < Offsets: : UseSlowRCShift) / / highest processing | (BitsType (1) < < Offsets: : SideTableMarkShift)) {/ / 62th processing assert (refcountIsInline); }Copy the code
- By implementing this function, we can apply the
weak
The second address is processed
- To address
0xc000000020b86cde
Process, first wipe the top two digits 0, and then move it 3 bits to the left to get the address0x105C366F0
.lldb
Viewing Memory Data
- So be
weak
The final structure of the modified class looks like this
HeapObject{
InlineRefCountBits{ strong count + unowned count}
HeapObjectSideTableEntry{
HeapObjec *object
xxx
strong count + unonwned count (uint64_t)
weak count (uint32_t)
}
}
Copy the code
weak
Modify the object againretain
_swift_retain_
Method executes down
- in
incrementSlow
It’s also true in the functionside
The operation of the
template <typename RefCountBits> void RefCounts<RefCountBits>::incrementSlow(RefCountBits oldbits, uint32_t n) { if (oldbits.isImmortal(false)) { return; } else if (oldbits.hasSideTable()) { // Out-of-line slow path. auto side = oldbits.getSideTable(); side->incrementStrong(n); } else { // Retain count overflow. swift::swift_abortRetainOverflow(); }}Copy the code
A circular reference
- Anti-initializer
deinit
, similar to ocdealloc
- Resolve the use of circular references
weak
- use
unowned
weak
andunowned
The difference between,unowned
The modified variables are assumed to have values during program execution, similar to those in OCunsafe_unretained
It is not safe to use wild Pointers in cases where the lifetime of a variable can be determined to be controllable, as used in closures above
Capture the list
- Before the parameter list is defined, the capture list is written as a list of expressions connected by commas and enclosed in square brackets. If a capture list is used, it must be used even if the parameter name, parameter type, and return type are omitted
in
The keyword - Take an example to understand the capture list
- For each constant in the capture list, the closure initializes the constants defined in the capture list using constants or variables with the same name in the surrounding scope.