采用引用计数的ARC内存管理方案(针对堆空间)
三种引用
强引用 默认情况下 引用都是强引用
弱引用 通过weak定义弱引用
- 必须是可选类型的var 因为实例销毁后 ARC会自动将弱引用设置为nil
- ARC自动给弱引用设置nil时 不会触发属性观察器
无主引用 通过unowned定义无主引用
- 不会产生强引用 可以是非可选类型 实例销毁后仍然存储着实例的内存地址(类似于OC中的unsafe_unretained)
- 试图在实例销毁后访问无主引用 会产生运行时错误(野指针)
class Person {
deinit {
print("Person.deinit")
}
}
func test() {
let p = Person()
}
test()
class Dog{
}
class Person {
weak var dog: Dog? {
willSet {}
didSet{}
}
deinit {
print("Person.deinit")
}
}
var p = Person()
p.dog = nil
weak unowned的使用限制
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?
协议遵守AnyObject 只能被类遵守
AutoreleasePool
public func autoreleasepool<Result>(invoking body: () throws -> Result) rethrows -> Result
class Person {
deinit {
print("Person.deinit")
}
func run(_ v: Int) {
print("fun run",v)
}
}
autoreleasepool {
let p = Person()
p.run(10)
}
循环引用
weak unowned 都能解决循环引用的问题 但是 unowned比weak少一些性能消耗
weak unowned 何时使用
1.在声明周期中可能会变为nil 的使用weak来修饰
2.初始化赋值后再也不会变为nil的使用unowned修饰
闭包的循环引用
闭包表达式默认会对用到的外层对象产生额外的强引用(对外层对象进行了retain操作)
class Person {
var fn: (() -> ())?
func run() {
print("run")
}
deinit {
print("deinit")
}
}
func test() {
let p = Person()
p.fn = { [weak p] in
p?.run()
}
}
test()
[weak p] 是捕获列表
如果想在定义闭包属性的同时引用self 这个闭包必须是lazy的 (因为在实例初始化完毕之后才能引用self)
class Person {
lazy var fn: ( () -> ()) = {
[weak self] in
self?.run()
}
func run() {
print("run")
}
deinit {
print("deinit")
}
}
func test() {
let p = Person()
p.fn()
}
test()
如果闭包中用到了实例成员(属性 方法) 编译器会强制要求明确写出self
如果lazy属性是闭包调用的结果 那么不用考虑循环引用的问题(因为闭包调用后 闭包的声明周期就结束了)
class Person {
var age: Int = 0
lazy var getAge: Int = {
age
}()
deinit {
print("deinit")
}
}
func test() {
let p = Person()
print(p.getAge)
}
test()
@escaping
非逃逸闭包 逃逸闭包 一般都是当做参数传递给函数
非逃逸闭包
非逃逸闭包 闭包调用发生在函数结束之前 闭包调用在函数作用域内
func test(_ fn: () -> () ){
fn()
}
test{
print(1)
}
逃逸闭包
逃逸闭包 闭包有可能在函数结束后调用 闭包调用逃离了函数的作用域 需要通过@escaping声明
typealias Fn = () -> ()
var gFn: Fn?
func test(_ fn: @escaping Fn ) {
gFn = fn
}
func test2(_ fn: @escaping Fn ) {
DispatchQueue.global().async {
fn()
}
}
强制加@escaping修饰 是为了安全 提醒是逃逸闭包 调用有可能会发生在函数结束之后 若闭包里面使用了函数里面的参数 会不安全
typealias Fn = () -> ()
class Person {
var fn: Fn
//fn 是逃逸闭包
init(fn: @escaping Fn) {
self.fn = fn
}
func run() {
//DispatchQueue.global().async 是一个逃逸闭包
//用到了实例成员(属性 方法) 编译器会强制要求明确写出self
DispatchQueue.global().async {
[weak self] in
self?.fn()
}
}
static func eat() {
print("eat")
}
deinit {
print("deinit")
}
}
func test(){
let p = Person(fn: Person.eat)
p.run()
}
test()
逃逸闭包的注意点
inout参数不能被逃逸闭包捕获
typealias Fn = () -> ()
func other1(_ fn: Fn){
fn()
}
func other2(_ fn: @escaping Fn) {
fn()
}
func test(value: inout Int) -> Fn {
other1 {
value += 1
}
other2 {
//Escaping closures can only capture inout parameters explicitly by value
value += 1
}
func plus() {
value += 1
}
//Escaping closures can only capture inout parameters explicitly by value
return plus
}
func test(value: inout Int) {
other1 {
value += 1
}
}
网友评论