强引用
// 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);
};
所以内存管理这些里的信息就是
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
对于捕获列表中的每个常量,闭包会利⽤周围范围内具有相同名称的常量或变量,来初始化捕获列表中定义的常量。