Swift pointer

  • SwiftThere are two types of Pointers intyped pointerSpecify a pointer to the data type,raw pointerNo specified data type pointer (native pointer)
  • raw pointerinSwiftIs represented byUnsafeRawPointer
  • tyepd pointerinSwiftIs represented byUnsafePointer<T>
  • SwiftThe 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 Pointersraw pointerHow 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 creationType PointerThe 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.
  • assumingMemoryBoundAssuming memory binding, this tells the compiler: I am this type, don’t check me any more.

Swift Memory Management

  • SwiftTo 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
  • throughlldbDebug prints its reference count

Source code analysis reference count

  • We analyze it by reading the source code directlyrefCountedWe knowHeapObjectThere are two properties in thereHeapMetadata const *metadata;andInlineRefCounts refCounts

  • refCountsThe reference counting properties,InlineRefCountsIt’s an alias
typedef RefCounts<InlineRefCountBits> InlineRefCounts;
Copy the code
  • RefCountsIs a template class whose structure is determined by the parameters passed in

  • InlineRefCountBitsAnother alias
typedef RefCountBitsT<RefCountIsInline> InlineRefCountBits;
Copy the code
  • RefCountIsInlineIs aenum
enum RefCountInlinedness { RefCountNotInline = false, RefCountIsInline = true };
Copy the code
  • RefCountBitsTStructure, which has one parameterBitsType bits.BitsTypeisRefCountBitsInt<refcountIsInline, sizeof(void*)>::TypeThe alias

  • RefCountBitsIntBits 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

  • OffsetsThe 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 codeswift_retainTo findswift_retainYou can see that it is executedobject->refCounts.increment(1);function

  • incrementThe implementation of theincrementStrongExtraRefCountfunction
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
  • functionincrementStrongExtraRefCount, will pass the parameter into strongBitsTypeThrough the above study, we know that it is actuallyuint64_tType, 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 variableweak 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 useweakModifier is not allowed to be set to nil
  • Using theweakAnd then after that, let’s look at itrefcountAs you can seerefcountChanged.

  • So let’s look at thatweakWhat did you do in theweakTo set a breakpoint, run into the breakpoint

  • We have studied it aboveswift_retain.swift_releaseandswift_retainIt’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
  • HeapObjectSideTableEntryThere are two properties in the classobjectandrefCounts

  • SideTableRefCountsThe 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 theweakThe second address is processed

  • To address0xc000000020b86cdeProcess, first wipe the top two digits 0, and then move it 3 bits to the left to get the address0x105C366F0.lldbViewing Memory Data

  • So beweakThe 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

weakModify the object againretain

  • _swift_retain_Method executes down

  • inincrementSlowIt’s also true in the functionsideThe 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-initializerdeinit, similar to ocdealloc

  • Resolve the use of circular referencesweak

  • useunowned

  • weakandunownedThe difference between,unownedThe modified variables are assumed to have values during program execution, similar to those in OCunsafe_unretainedIt 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 omittedinThe 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.