跟OC一样,Swift也是采用基于引用计数的ARC内存管理方案(针对堆空间),Swift中的ARC有三种引用:
强引用
(strong reference): 默认情况下都是强引用
若引用
(weak reference):通过weak
定义弱引用
必须是可选类型的va
r,因为实力销毁后,ARC会将自动弱引用置位nil
ARC自动给弱引用设置为nil
时,不会触发属性观察
无主引用
(unowned reference):通过unowned
定义无主引用
不会产生强引用,实例销毁后仍然存储着实例的内存地址(类似OC中的unsafe_unretained
)
试图在实例销毁后访问无主引用,会产生运行时错误(野指针)
1. weak、unowned的使用限制
-
weak、unowned
只能用在类实例上面
protocol Livable: AnyObject {}//协议继承AnyObject后只能被类继承
class Person {}
weak var p0 : Person?
weak var p1: AnyObject?
weak var p2: Livable?
unowned var p10 : Person?
unowned var p11: AnyObject?
unowned var p12: Livable?
2. 循环引用
weak、unowned
都能解决循环引用的问题,unowned要比weak 少一些性能消耗
- 在生命周期中有可能变为
nil
的使用weak
- 初始化赋值之后,再也不会变为
nil
的使用unowned
闭包的循环引用:
- 闭包表达式默认会对用到的外层对象产生额外的强引用(对外层对象进行returen操作)
class Person {
var fn: (() -> ())?
func run() {
print("person run")
}
deinit {
print("person deinit")
}
}
func tt() {
print("tt")
}
func test() {
var p = Person()
p.fn = {p.run()}
}
test()
注意
:这里并不会看到打印person deinit。因为 var p = Person(),Person对象p强引用fn闭包。当执行完 p.fn = {p.run()}时,闭包又强引用Person对象,造成循环引用
在闭包表达式的捕获列表声明weak或unowned引用,可解决循环引用问题
func test() {
var p = Person()
p.fn = {
[weak p] in
p?.run()
}
}
或
func test() {
var p = Person()
p.fn = {
[unowned p] in
p.run()
}
}
- 如果想定义闭包属性的同时引用
self
,这个闭包必须是lazy
的(因为在实例初始化完毕之后才能引用self
)
lazy var fn: (() -> ()) = {
// [weak self] in
// self?.run()
[weak weakSelf = self] in
weakSelf?.run()
}
- 如果
lazy
属性是闭包调用的结果,那么不用考虑循环引用的问题(因为闭包调用结束后,闭包的生命周期就结束了)
class Person {
var age: Int = 10
lazy var getAge: Int = {
self.age
}()
}
3. 内存访问冲突
内存访问冲突会在两个访问满足下列条件时发生:
- 至少一个是写入操作
- 他们访问的是同一块内存
- 他们的访问时间重叠(比如在同一个函数中)

解决方案:

如果下面的条件满足,就说明重叠访问结构体的属性时安全的:
- 你只访问实例存储属性,不是计算属性或者类属性
- 结构体时局部变量而非全局变量
- 结构体要么没有被闭包捕获要么纸杯费逃逸闭包捕获
网友评论