Swift指针
Swift
中的指针分为两类,typed pointer
指定数据类型指针,raw pointer
未指定数据类型指针(原生指针)raw pointer
在Swift
中的表示是UnsafeRawPointer
tyepd pointer
在Swift
中的表示是UnsafePointer<T>
Swift
指针对比oc指针 | Swift | oc | 说明 |
| --- | --- | --- | | UnsafePointer | const T * | 指针及所指向的内容都不可变 |
| UnsafeMutablePointer | T * | 指针及所指向的内容都可变 |
| UnsafeRawPointer | const void * | 指针及所指向的内容都不可变且指针指向未知类型 |
| UnsafeMutableRawPointer | void * | 指针及所指向的内容可变且指针指向未知类型 |
原生指针
- 我们想在内存中存储连续4个整型数据,通过原生指针
raw pointer
如何实现
// 1,分配32字节内存大小
let p = UnsafeMutableRawPointer.allocate(byteCount: 32, alignment: 8)
// 2、advanced代表当前p前进的步长,对于rawPointer来说我们需要移动的是当前存储值的内存大小即MemoryLayout.stride
// storeBytes:这里就是存储我们的数据,这里需要指定数据的类型
for i in 0..<4 {
p.advanced(by: i * 8).storeBytes(of: (i + 1), as: Int.self)
}
// 3、load: 加载 fromByteOffset:是相对于我们当前p的首地址的偏移
for i in 0..<4 {
let value = p.load(fromByteOffset: i * 8, as: Int.self)
print("index:\(i)--value:\(value)")
}
p.deallocate()
指定数据类型指针
- 获取一个变量的地址
// 获取age变量的地址
var age = 10
// 通过Swift提供的简写API,这里注意当前尾随闭包的写法
let p = withUnsafePointer(to: &age, {$0})
// withUnsafePointer(to: age, {$0})
print(p.pointee)
// withUnsafePointer的返回值是unsafePointer,意味着我们不能直接修改值,此时b的值=22
var b = withUnsafePointer(to: &age, { ptr in
ptr.pointee + 12
})
print(b)
// 如果我们想要修改Pointer.pointee的值,使用withUnsafeMutablePointer
withUnsafeMutablePointer(to: &age, { ptr in
ptr.pointee += 12
print(ptr.pointee)
})
- 另一种创建
Type Pointer
的方式
struct XQTeacher {
var age = 18
var height = 1.88
}
// capacity:容量大小,当前的大小为2*MemoryLayout<XQTeacher>.stride
let p = UnsafeMutablePointer<XQTeacher>.allocate(capacity: 2)
// 初始化当前的UnsafeMutablePointer<XQTeacher>指针
p.initialize(to: XQTeacher())
p.advanced(by: 1).initialize(to: XQTeacher.init(age: 20, height: 1.90))
p.deinitialize(count: 2)
print(p[0])
print(p[1])
p.deallocate()
通过案例来理解指针
- 1,通过指针绑定到swift_class结构体
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 {
var age = 18
}
//实例变量的内存地址
var t = XQTeacher()
//Unmanagedpass.Unretained(t as AnyObject).toOpaque()
//OC 和 CF 交互的方式, __brige ,所有权的转换
let ptr = Unmanaged.passUnretained(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: 1)
print(metaPtr.pointee)
// 打印结果
// xq_swift_class(kind: 0x0000000100008140, superClass: 0x00007fff889728f8, cachedata1: 0x00007fff201f0af0, cachedata2: 0x0000802000000000, data: 0x0000000100606fe2, flags: 2, instanceAddressOffset: 0, instanceSize: 24, instanceAlignMask: 7, reserved: 0, classSize: 136, classAddressOffset: 16, description: 0x0000000100003c3c)
- 2,元组指针类型转换
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")
}
- 3,获取结构体的属性的指针
struct HeapObject {
var strongRef = 10
var unownedRef = 20
}
var h = HeapObject()
withUnsafePointer(to: &h, { (ptr : UnsafePointer<HeapObject>) in
// 通过原生指针 + 偏移量
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")
}
withMemoryRebound
:临时更改内存绑定类型,使用场景:函数调用的参数需要传递一个UnsafePointer<UInt64>
类型参数,我们传入的age其实是一个UnsafePointer<Int>
且不想修改它的类型就可以使用临时更改绑定类型,只在闭包的作用空间里面有效
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")
}
bindMemory(to: capacity:)
:更改内存绑定类型,如果之前没有绑定,那么就是首次绑定;如果绑定过了,会被重新绑定为该类型。assumingMemoryBound
:假定内存绑定,这里是告诉编译器:我就是这类型,不用再检查我了。
Swift内存管理
Swift
中使用自动引用计数(ARC)机制管理内存,我们创建一个类来观察它的引用引用计数
class XQTeacher {
var age = 18
var name = "xq"
}
var t = XQTeacher()
var t1 = t
var t2 = t
- 通过
lldb
调试打印它的引用计数
源码分析引用计数
- 我们通过直接阅读源码的方式来分析
refCounted
,我们知道HeapObject
里面有两个属性HeapMetadata const *metadata;
和InlineRefCounts refCounts
refCounts
就是引用计数相关的属性,InlineRefCounts
是一个别名
typedef RefCounts<InlineRefCountBits> InlineRefCounts;
RefCounts
是一个模板类,它的结构由传入的参数决定InlineRefCountBits
又是一个别名
typedef RefCountBitsT<RefCountIsInline> InlineRefCountBits;
RefCountIsInline
是一个enum
enum RefCountInlinedness { RefCountNotInline = false, RefCountIsInline = true };
RefCountBitsT
的结构,里面有一个参数BitsType bits
,BitsType
是RefCountBitsInt<refcountIsInline, sizeof(void*)>::Type
的别名RefCountBitsInt
的结构,所以bits其实就是一个uint64_t的类型的数据
struct RefCountBitsInt<RefCountNotInline, 4> {
typedef uint64_t Type;
typedef int64_t SignedType;
};
- 回到类初始化的时候,
enum Initialized_t { Initialized };
的初始化 Offsets
的定义
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);
};
- 最终我们可以得到下面结论
swift_retain
-
在源码中搜索
swift_retain
,找到swift_retain
的实现,可以看到它是执行了object->refCounts.increment(1);
函数 -
increment
的实现,调用了incrementStrongExtraRefCount
函数
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));
}
- 函数
incrementStrongExtraRefCount
的实现,将传入的参数强转成BitsType
,通过上文的研究我们知道其实就是uint64_t
类型,再进行位移操作StrongExtraRefCountShift
。
// 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);
}
弱引用
- 弱引用声明一个变量
weak var t = XQTeacher()
,可以看到它是一个可选值,因为程序运行过程中是允许将当前变量设置为nil的。 - 可以看到如果不使用
weak
修饰,是不允许设置为nil的 - 使用了
weak
修饰之后,我们再来观察下refcount
,可以看到refcount
变了。 - 我们来研究下
weak
做了什么,在weak
处设置断点,运行进入断点 - 在上文我们已经研究了
swift_retain
,swift_release
和swift_retain
是对应的,我们重点研究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);
}
👇
// SideTableRefCountBits specialization intentionally 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;
}
HeapObjectSideTableEntry
类里面有两个属性object
和refCounts
SideTableRefCounts
的定义- SideTableRefCountBits的结构
auto newbits = InlineRefCountBits(side);
函数调用了下面的初始化函数。
LLVM_ATTRIBUTE_ALWAYS_INLINE
RefCountBitsT(HeapObjectSideTableEntry* side)
: bits((reinterpret_cast<BitsType>(side) >> Offsets::SideTableUnusedLowBits)// 右移3位
| (BitsType(1) << Offsets::UseSlowRCShift) //最高位处理
| (BitsType(1) << Offsets::SideTableMarkShift))// 第62位处理
{
assert(refcountIsInline);
}
- 通过这个函数的实现,我们可以对
weak
第二个地址进行处理 - 对地址
0xc000000020b86cde
处理,先将最高两位抹0,再将它左移3位,得到地址0x105C366F0
,lldb
查看内存数据 - 所以被
weak
修饰的类的最终结构会变成下面这样
HeapObject{
InlineRefCountBits{ strong count + unowned count}
HeapObjectSideTableEntry{
HeapObjec *object
xxx
strong count + unonwned count (uint64_t)
weak count (uint32_t)
}
}
weak
修饰的对象再进行retain
_swift_retain_
方法往下执行- 在
incrementSlow
函数里面也是对side
的操作
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();
}
}
循环引用
- 反初始化器
deinit
,类似于oc的dealloc
- 解决循环引用问题使用
weak
- 使用
unowned
weak
和unowned
的区别,unowned
修饰的变量在程序运行期间都是假定有值的,类似于oc中的unsafe_unretained
,是不安全的可能会出现野指针的情况,在能确定变量的生命周期是可控,像上文中闭包中的使用
捕获列表
- 定义在参数列表之前,捕获列表被写为用逗号连接起来的表达式列表,并用方括号括起来。如果使用捕获列表,则即使省略参数名称,参数类型和返回类型,也必须使用
in
关键字 - 通过一个例子来理解捕获列表
- 对于捕获列表中的每个常量,闭包会利用周围范围内具有相同名称的常量或变量,来初始化捕获列表中定义的常量。