swift--内存管理

强引用

// 32-bit out of line
template <>
struct RefCountBitsInt {
  typedef uint64_t Type;
  typedef int64_t SignedType;
};

template 
class RefCountBitsT {

  typedef typename RefCountBitsInt::Type
    BitsType;
  BitsType bits;
...
}

引用计数操作的实际就是这个64位整形。

  // Initialize a HeapObject header as appropriate for a newly-allocated object.
  constexpr HeapObject(HeapMetadata const *newMetadata) 
    : metadata(newMetadata)
    , refCounts(InlineRefCounts::Initialized)
  { }


  // Refcount of a new object is 1.
  constexpr RefCounts(Initialized_t)
    : refCounts(RefCountBits(0, 1)) {}

LLVM_ATTRIBUTE_ALWAYS_INLINE
  constexpr
  RefCountBitsT(uint32_t strongExtraCount, uint32_t unownedCount)
    : bits((BitsType(strongExtraCount) << Offsets::StrongExtraRefCountShift) |
           (BitsType(unownedCount)     << Offsets::UnownedRefCountShift))
  { }


struct RefCountBitOffsets<8> {
  static const size_t IsImmortalShift = 0;
  static const size_t IsImmortalBitCount = 1;
  static const uint64_t IsImmortalMask = maskForField(IsImmortal);

  static const size_t UnownedRefCountShift = shiftAfterField(IsImmortal);
  static const size_t UnownedRefCountBitCount = 31;
  static const uint64_t UnownedRefCountMask = maskForField(UnownedRefCount);

  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);
};

所以内存管理这些里的信息就是


内存管理.png

strongRetain在swift源码中调用的是_swift_retain_

static HeapObject *_swift_retain_(HeapObject *object) {
  SWIFT_RT_TRACK_INVOCATION(object, swift_retain);
  if (isValidPointerForNativeRetain(object))
    object->refCounts.increment(1);
  return object;
}


  // Increment the reference count.
  void increment(uint32_t inc = 1) {
    auto oldbits = refCounts.load(SWIFT_MEMORY_ORDER_CONSUME);
    //64位整形
    RefCountBits newbits;
    do {
      newbits = oldbits;
      bool fast = newbits.incrementStrongExtraRefCount(inc);
      if (SWIFT_UNLIKELY(!fast)) {
        if (oldbits.isImmortal())
          return;
        return incrementSlow(oldbits, inc);
      }
    } while (!refCounts.compare_exchange_weak(oldbits, newbits,
                                              std::memory_order_relaxed));
  }

  bool incrementStrongExtraRefCount(uint32_t inc) {
    // This deliberately overflows into the UseSlowRC field.
    bits += BitsType(inc) << Offsets::StrongExtraRefCountShift;
    return (SignedBitsType(bits) >= 0);
  }

所以每次都会加上二进制的偏移,我们每次看到的0x0000000200000002就是这样来的,所以每次强引用都会加上0x0000000200000000

弱引用

var t = Teacher()
weak var t1 = t

当我们打印t的引用计数的时候发现还是2,并没有加1,并且t1的类型变成了可选类型,防止t释放变为nil.
通过触发断点我们可以看到通过weak触发的 swift_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);
  }

HeapObjectSideTableEntry* RefCounts::formWeakReference()
{
  auto side = allocateSideTable(true);
  if (side)
    return side->incrementWeak();
  else
    return nullptr;
}


HeapObjectSideTableEntry* RefCounts::allocateSideTable(bool failIfDeiniting)
{
...

  HeapObjectSideTableEntry *side = new HeapObjectSideTableEntry(getHeapObject());
  
  auto newbits = InlineRefCountBits(side);
...
}

 RefCountBitsT(HeapObjectSideTableEntry* side)
    : bits((reinterpret_cast(side) >> Offsets::SideTableUnusedLowBits)
           | (BitsType(1) << Offsets::UseSlowRCShift)
           | (BitsType(1) << Offsets::SideTableMarkShift))
  {
    assert(refcountIsInline);
  }

然后直接将偏移后的SideTable 存储到bits


class alignas(sizeof(void*) * 2) SideTableRefCountBits : public RefCountBitsT
{
  uint32_t weakBits;
}

SideTableRefCountBits继承了RefCountBitsT,而RefCountBitsT存放着之前的64位信息,又多了一个32位的 weakBits
我们可以看看当前弱引用之后的t的refCount

(lldb) po t

(lldb) x/4g 0x100549f80
0x100549f80: 0x0000000100003250 0x0000000200000002
0x100549f90: 0x000000000000000a 0x0000000000000000

//添加一个弱引用
(lldb) x/4g 0x100549f80
0x100549f80: 0x0000000100003250 0xc0000000200eb538
0x100549f90: 0x000000000000000a 0x0000000000000000
//reinterpret_cast(side) >> Offsets::SideTableUnusedLowBits
//SideTableUnusedLowBits = 3
(lldb) p/x 0x200eb538 << 3
(Int) $R16 = 0x000000010075a9c0

(lldb) x/4g 0x000000010075a9c0
0x10075a9c0: 0x0000000100549f80 0x0000000000000000
0x10075a9d0: 0x0000000200000002 0x0000000000000002

可以看到第一个位置指向的就是t的地址,最后的0x0000000000000002就是弱引用计数。

unowned(无主引用)

var t = Teacher()
unowned var t1 = t

调用t1的时候Teacher是确定类型他不可以设置为nil,有野指针的问题,所以要确保他一直有值。

循环引用

闭包⼀般默认捕获我们外部的变量

var age = 10 
let closure = { 
  age += 1 
 } 
  closure() 
print(age)//打印为 11

从输出结果来看: 闭包内部对变量的修改将会改变外部原始变量的值 如果我们把上⾯的例⼦修改⼀下

class Teacher{
    var age = 10
    var complectionBack: (() -> ())?
    deinit {
        print("Teacher deinit")
    }
}
var t = Teacher()
unowned var t1 = t

t.complectionBack = {
     t.age += 1
    
}

print("end")

这样就会造成循环引用,当我们添加 unowned 或者weak修饰就可以解决循环引用。要注意的weak修饰的是可选类型的,所以修饰的对象要加?

在 Swift 中叫做 捕获列表

var age = 0
var height = 0.0
let closure = { [age] in
    print(age)
    print(height)
    
}
age = 10
height = 1.85
closure()
··················
0
1.85

对于捕获列表中的每个常量,闭包会利⽤周围范围内具有相同名称的常量或变量,来初始化捕获列表中定义的常量。

你可能感兴趣的:(swift--内存管理)