内存管理
- 跟 OC 一样,Swift 也是采用基于引用计数的 ARC 内存管理方案(针对堆空间)
- Swift 中的 ARC 有3种引用
- 强引用:默认情况下,引用都是强引用
- 弱引用:通过 weak 定义弱引用
- 必须是可选类型的 var,因为实例销毁后,ARC 会自动将弱引用设置为 nil
- ARC 自动给弱引用设置为 nil 时,不会触发属性观察器
- 无主引用:通过 unowned 定义无主引用
- 不会产生强引用,非可选类型,实例销毁后仍然存储着实例的内存地址(和 oc 的 unsafe_unretained 类似)
- 试图在实例销毁后访问无主引用,会产生运行时错误(野指针)
weak、unowned 的使用限制
- weak、unowned 只能用在类实例上面
Autoreleasepool
public func autoreleasepool<Result>(invoking body: () throws -> Result) rethrows -> Result
循环引用(Reference Cycle)
- weak、unowned 都能解决循环引用的问题,unowned 要比 weak 少一些性能消耗
- 在生命周期可能会变为 nil 的使用 weak
- 初始化赋值后再也不会变为 nil 的使用 unowned
闭包的循环引用
- 闭包表达式默认会对用到的外层对象产生额外的强引用
- 这个参考 oc 中的 block 就可以了
@escaping
- 逃逸闭包
- 不允许捕获输入输出参数
内存访问冲突
- 内存访问冲突会在两个访问满足下列条件时发生:
- 至少一个是写入操作
- 它们访问的是同一块内存
- 它们访问时间重叠
- 怎么解决?可以利用 Copy In Copy Out
Simultaneous accesses to 0x1006469c0, but modification requires exclusive access.
var step = 1
func increment(_ num: inout Int) {
num += step
}
increment(&step)
解决方案
var t = step
increment(&t)
step = t
指针
- UnsafePointer <-> const Pointee *
- UnsafeMutablePointer <-> Pointee *
- UnsafeRawPointer <-> const void *
- UnsafeMutableRawPointer <-> void *
获取指向某个变量的指针
- withUnsafeMutablePointer(to: &age) { $0 }
- withUnsafePointer(to: &age) { $0 }
@inlinable public func withUnsafeMutablePointer<T, Result>(to value: inout T, _ body: (UnsafeMutablePointer<T>) throws -> Result) rethrows -> Result {
body(to)
}
var age = 10
var ptr = withUnsafeMutablePointer(to: &age) { $0 }
ptr.pointee = 20
print(age)
var ptr1 = withUnsafeMutablePointer(to: &age) { UnsafeMutableRawPointer($0) }
ptr1.storeBytes(of: "a", as: String.self)
print(age)
---------------执行结果---------------
20
97
获得指向某个堆空间实例的指针
- withUnsafePointer(to: &person) { UnsafeRawPointer($0) }
- UnsafeMutableRawPointer(bitPattern: ptr.load(as: UInt.self))
class Person {
var age: Int = 20
}
var person = Person()
var ptr = withUnsafePointer(to: &person) { UnsafeRawPointer($0) }
var ptr1 = UnsafeMutableRawPointer(bitPattern: ptr.load(as: UInt.self))
print(ptr, ptr1)
---------------执行结果---------------
0x00007ffeefbff428 Optional(0x0000000100712200)
创建指针
- UnsafeMutableRawPointer.allocate(byteCount: 16, alignment: 1)
- deallocate()
- malloc(16)
- free(ptr)
上面两种都可以创建
var ptr = malloc(16)
ptr?.storeBytes(of: 11, as: Int.self)
ptr?.storeBytes(of: 22, toByteOffset: 8, as: Int.self)
print(ptr?.load(as: Int.self))
print(ptr?.load(fromByteOffset: 8, as: Int.self))
free(ptr)
class Person {
var age: Int = 0
var name: String = ""
init(age: Int, name: String) {
self.age = age
self.name = name
}
deinit {
print("deinit")
}
}
var ptr = UnsafeMutablePointer<Person>.allocate(capacity: 1)
defer {
ptr.deinitialize(count: 1)
ptr.deallocate()
}
ptr.initialize(to: Person(age: 10, name: "json"))
print(ptr[0].age)
HandyJson 中大量使用了这个指针,后面我给会写博客具体解读他。
指针之间的转换
- unsafeBitCast 是忽略数据类型的强制转换,不会因为数据类型的变化而改变原来的内存数据
- 也就是说你内存中原来是什么,转换之后的还是什么
- 如果用 unsafeBitCast 强转,强转后的字节数不同,那么会报错
var ptr = UnsafeMutableRawPointer.allocate(byteCount: 16, alignment: 1)
ptr.assumingMemoryBound(to: Int.self).pointee = 11
(ptr + 8).assumingMemoryBound(to: Double.self).pointee = 20.0
print(unsafeBitCast(ptr, to: UnsafePointer<Int>.self).pointee)
print(unsafeBitCast(ptr + 8, to: UnsafePointer<Double>.self).pointee)
var age = -1
var age1 = unsafeBitCast(age, to: UInt.self)
print(age, age1)
-----------------------执行结果-----------------------
(lldb) x/wg 0x7ffeefbff438
0x7ffeefbff438: 0xffffffffffffffff
(lldb) x/wg 0x7ffeefbff430
0x7ffeefbff430: 0xffffffffffffffff
-1 18446744073709551615
思考
-
swift 中的 weak 变量是怎么被初始化的?
-
获取对象堆内存的地址有没有更简单的方法?
var ptr = unsafeBitCast(person, to: UnsafeRawPointer.self) 这个就相当于将 person 里面存储的地址强转换成 UnsafeRawPointer 类型