跟OC一样,Swift也是采取基于引用技术的ARC内存管理方案(针对堆空间) Swift的ARC中有3中引用:
强引用(strong reference):默认情况下,引用都是强引用 弱引用(weak reference):通过weak定义弱引用
必须是可选类型的var,因为实力销毁后,ARC户自动将弱引用设置为nil ARC自动给弱引用设置nil时,不会触发属性观察器
无主引用(unowned reference):通过unowned定义无主引用
不会产生强引用,实例销毁后任然存储着实例的内存地址(类似于OC中的unsafe_unretained) 视图在实力销毁后访问无主引用,会产生运行时错误(野指针) Fatal error:Attempted to read an unowned reference but object 0x0 was already deallocated
weak、unowned的使用限制
protocol Livable : 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 ?
Autoreleasepool
public func autoreleasepool< Result > ( invoking body: ( ) throws - > Result ) rethrows - > Result
autoreleasepool {
let p = MyPerson ( age: 20 , name: "Jack" )
p. run ( )
}
循环引用(Reference Cycle)
weak、unowned都能解决循环引用的问题,unowned要比weak少一些性能消耗 在声明周期中可能会变为nil的使用weak 初始化赋值后再也不会变为nil的使用unowned
闭包的循环引用
闭包表达式默认回对用到的外层对象产生额外的强引用(对外层对象进行了retain操作) 下面代码会产生循环引用,导致Person对象无法释放(看不到Person的deinit被调用)
class Person {
var fn: ( ( ) - > ( ) ) ?
func run ( ) { print ( "run" ) }
deinit { print ( "deinit" ) }
}
func test ( ) {
let p = Person ( )
p. fn = { p. run ( ) }
}
test ( )
``
- 在闭包表达式的捕获列表声明weak 或者unowned 引用,解决循环引用问题
```swift
p. fn = {
[ weak p] in
p? . run ( )
}
p. fn = {
[ unowned p] in
p. run ( )
}
p. fn = {
[ weak wp = p, unowned up = p, a = 10 + 20 ] in
wp? . run ( )
}
如果想在定义闭包属性的同事引用self,这个闭包必须是lazy的(因为在初始化完毕之后才能引用self) 下面的闭包fn内部如果用到了实例成员(属性、方法) 编译器会强制要求明确写出self
class Person {
lazy var fn: ( ( ) - > ( ) ) = {
[ weak self ] in
self ? . run ( )
}
func run ( ) { print ( "run" ) }
deinit { print ( "deinit" ) }
}
如果lazy属性是闭包调用的结果,那么不用考虑循环引用的问题(因为闭包调用后,闭包的生命周期就结束了)
class Person {
var age: Int = 0
lazy var getAge: Int = {
self . age
} ( )
deinit { print ( "deinit" ) }
}
@escaping
非逃逸闭包、逃逸闭包,一般都是当做参数传递给函数 非逃逸闭包:闭包调用发生在函数结束前,闭包调用在函数作用域内 逃逸闭包:闭包有可能再函数结束后调用,闭包调用逃离了函数的作用域,需要通过@escaping声明
import Dipatch
typealias Fn = ( ) - > ( )
func test1 ( _ fn: Fn ) { fn ( ) }
var gFn: Fn ?
func test2 ( _ fn: @escaping Fn ) { gFn = fn }
func test3 ( _ fn: @escaping Fn ) {
DispatchQueue . global ( ) . async {
fn ( )
}
}
class Person {
var fn: Fn
init ( fn: @escaping Fn ) {
self . fn = fn
}
func run ( ) {
DispatchQueue . global ( ) . async {
self . fn ( )
}
}
}
内存访问冲突
至少一个是写入操作 它们访问的是同一块内存 它们的访问时间重叠(比如在同一个函数内)
func plus ( _ num: inout Int ) - > Int { num + 1 }
var number = 1
number = plus ( & number)
var step = 1
func increment ( _ num: inout Int ) { num + = step }
increment ( & step)
var copyOfStep = step
increment ( & copyOfStep)
step = copyOfStep
func balance ( _ x: inout Int , _ y: inout Int ) {
let sum = x + y
x = sum / 2
y = sum - x
}
var num1 = 42
var num2 = 30
balance ( & num1, & num2)
balance ( & num1, & num1)
struct Player {
var name: String
var health: Int
var energy: Int
mutating func shareHealth ( with teammate: inout Player ) {
balance ( & teammate. health, & health)
}
}
var oscar = Player ( name: "Oscar" , health: 10 , energy: 10 )
var maria = Player ( name: "Maria" , health: 5 , energy: 10 )
oscar. shareHealth ( with: & maria)
oscar. shareHealth ( with: & oscar)
var tulpe = ( health: 10 , energy: 20 )
balance ( & tulpe. health, & tulpe. energy)
var holly = Player ( name: "Holly" , health: 10 , energy: 10 )
balance ( & holly. health, & holly. energy)
如果下面的条件可以满足,就说明重叠访问结构体的属性是安全的
你只访问实例存储属性,不是计算属性或者类型属性 结构体是局部变量而非全局变量 结构体要么没有被闭包捕获要么只被非逃逸闭包捕获
func test ( ) {
var tulpe = ( health: 10 , energy: 20 )
balance ( & tulpe. health, & tulpe. energy)
var holly = Player ( name: "Holly" , health: 10 , energy: 10 )
balance ( & holly. health, & holly. energy)
}
test ( )