指针不是安全的
1.野指针。指针指向对象释放,指针变为野指针
2.指针超出内存空间边界访问。如数组越界
3.原⽣指针和类型指针的转换,会有数据丢失
指针类型
Swift中的指针分为两类,
类型指针: typed pointer 指定数据类型指针,
原⽣指针: raw pointer 未指定数据类型的指针
原生指针
1.创建UnsafeMutableRawPointer.allocate
2.销毁指针deallocate()
3.储存值storeBytes(of: i, as: Int.self)
4.读取值load(fromByteOffset: i * MemoryLayout
3.移动指针advanced(by: i * MemoryLayout
func demo1() {
let p = UnsafeMutableRawPointer.allocate(byteCount: 4 * 8, alignment: 8)
defer {
print("32")
p.deallocate()
}
for i in 0..<4 {
p.advanced(by: i * MemoryLayout<Int>.stride).storeBytes(of: i, as: Int.self)
}
print(p)
for i in 0..<4 {
let value = p.load(fromByteOffset: i * MemoryLayout<Int>.stride, as: Int.self)
print(value)
}
}
类型指针
第⼀种⽅式就是直接分配内存
1.创建UnsafeMutablePointer<LGPerson>.allocate
2.销毁 p.deinitialize(count: 5) -> p.deallocate()
3.储存值p[0] = ...
4.读取值 pointee属性
let p = UnsafeMutablePointer<LGPerson>.allocate(capacity: 5)
defer {
p.deinitialize(count: 5)
p.deallocate()
}
p[0] = LGPerson(age: 18, sex: true, married: true)
p[1] = LGPerson(age: 19, sex: true, married: true)
print(p)
print(p.pointee)
第二种方式
通过已有变量获取
通过withUnsafePointer(to: &age) { $0.pointee + 21} 获取当前变量的地址
var age = 18
age =withUnsafePointer(to: &age) { ptr in
return ptr.pointee + 21
}
print(age)
指针绑定API
1.assumingMemoryBound(to:) 避免编译器类型检查
2.原生指针 bindMemory(to: capacity:) 更改内存绑定的类型。如果当前内存还没有类型绑定,则将⾸次绑定为该类型;否则重新绑定该类 型,并且内存中所有的值都会变成该类型
3.类型指针 withMemoryRebound(to: capacity: body:)指针使用过程中临时更改内存绑定类型
指针使用实例
1.解析类的数据结构
类的结构体指针内存布局
struct HeapObject {
var metadata: UnsafeRawPointer
var refCounted1: UInt32
var refCounted2: UInt32
}
struct Metadata{
var kind: Int
var superClass: Any.Type
var cacheData: (Int, Int)
var data: Int
var classFlags: Int32
var instanceAddressPoint: UInt32
var instanceSize: UInt32
var instanceAlignmentMask: UInt16
var reserved: UInt16
var classSize: UInt32
var classAddressPoint: UInt32
var typeDescriptor: UnsafeMutableRawPointer
var iVarDestroyer: UnsafeRawPointer
}
1. 获取实例的内存地址(原生指针) Unmanaged.passUnretained(t as AnyObject).toOpaque()
2.绑定具体类型(HeapObject)
let t = LGTeacher()
let objRawPtr = Unmanaged.passUnretained(t as AnyObject).toOpaque()
let objPtr = objRawPtr.bindMemory(to: HeapObject.self, capacity: MemoryLayout.stride(ofValue: t))
let tMetaData = objPtr.pointee.metadata.bindMemory(to: Metadata.self, capacity: MemoryLayout<Metadata>.stride)
print(MemoryLayout.stride(ofValue: t))
print(objPtr.pointee)
print(tMetaData.pointee)
print("end")
2.UInt64 与指针转换
1.UInt64 -> 指针 UnsafePointer
2.指针 -> UInt64 UInt64(bitPattern: Int64(Int(bitPattern: mhHeaderPtr)))
let mhHeaderPtr_IntRepresentation = UInt64(bitPattern: Int64(Int(bitPattern: mhHeaderPtr)))
var dataLoAddress = mhHeaderPtr_IntRepresentation + offset
var dataLoContent = UnsafePointer<UInt32>.init(bitPattern: Int(exactly: dataLoAddress) ?? 0)?.pointee
内存管理
Swift 中使⽤⾃动引⽤计数(ARC)机制来追踪和管理内存。
使用8字节存储当前的引⽤计数的。
weak 关键字,原来的 uint64_t 加上⼀个存储弱引⽤数的 uint32_t
1.正常情况InlineRefCountBits strong RC + unowned RC + flags 64位
2.加weak HeapObjectSideTableEntry strong RC + unowned RC + weak RC + flags 64+32
HeapObject {
isa
InlineRefCounts {
atomic
{ strong RC + unowned RC + flags
OR
HeapObjectSideTableEntry*
}
}
}
HeapObjectSideTableEntry {
SideTableRefCounts {
object pointer
atomic
{ strong RC + unowned RC + weak RC + flags
}
}
}
let t = LGTeacher()
let t1 = t
let t2 = t
print("end")
My Mac 调试的
1. MetaData 内存地址
2.引用计数
let t = LGTeacher(), strong RC + unowned RC + flags , strong RC 在33位是1, unowned RC在1位是1,flags在0位是1
Weak VS unowned
如果两个对象的⽣命周期完全和对⽅没关系(其中⼀⽅什么时候赋值为nil,对对⽅都没影响),请⽤ weak
如果你的代码能确保:其中⼀个对象销毁,另⼀个对象也要跟着销毁,这时候,可以(谨慎)⽤ unowned
常见闭包循环引用
1. 类有个complateCallback 闭包
2.complateCallback 闭包 使用实例 t.age
解决方法: [unowned t]
let t = LGTeacher()
t.complateCallback = { [unowned t] in
t.age += 1
}
闭包循环引用
不加unowned 或 weak ,不执行deinit{} 方法的
加入unowned 或 weak ,执行deinit{} 方法的
捕获列表
var age = 0
var height = 0.0
let closure = {[age] in
print(age)
print(height)
}
age =10
height =1.88
closure()
输出:age = 0 height = 1.88
[age] 捕获闭包之前的age值
height没有出现在捕获列表中,height值是闭包调用前height的值
Enum
内存大小
MemoryLayout
MemoryLayout
MemoryLayout
enum Weak: Int {
case MONDAY = 0
case TUEDAY
case WEDDAY
case THUDAY
case FRIDAY
case SATDAY
case SUNDAY
}
1. enum 的原始值rawValue,需要明确原始值类型
Weak.MONDAY.rawValue
Weak枚举内存大小 1字节
var a = Weak.MONDAY
MemoryLayout.size(ofValue: a) 1
MemoryLayout.stride(ofValue: a) 1
enum LGEnum {
// case test_one(Bool)
case test_two(Int)
case test_three
case test_four
}
MemoryLayout
MemoryLayout
enum 内存大小
关联值 所有参数内存大小 + 1字节
sizeof(Int) * 3 + sizeof(rawVlaue)
关键值indirect
enum List
{ case end
indirect case node(Element, next: List<Element>)
}
enum 前面加indirect,创建List枚举类型,调用swift_allocObject方法
indirect case node,是node时才调用swift_allocObject方法,end不调用的
Optional是一个枚举
enum MyOptional
{ case some(Value)
case none
}