您的位置:首页 > 移动开发 > Swift

Swift中的ARC(Automatic Reference Counting)

2017-06-07 17:26 459 查看
first of all参考文档

ARC(自动引用计数)

Swift使用”自动引用计数(Automatic Reference Counting)”来跟踪和管理引用程序中的内存使用情况。在大多数情况下,这意味着在Swift中内存不需要你自己来管理,当有些实例对象不再需要时(引用计数为0),ARC会自动释放实例对象使用的内存

ARC如何工作

每次创建一个类的新实例时,ARC会分配一大块内存来存储有关该实例的信息,该内存将保存有关实例类型的信息,已经与该实例关联的任何存储属性的值

另外,当不再需要实例时,ARC会释放该实例使用的内存,以便于将内存用于其他目的,这样可以确保类实例在不再需要时不占用内存空间,

但是,如果ARC要释放仍在使用的实例,那么将无法在访问该实例的属性,或者调用该实例的方法,实际上,如果你尝试访问该实例,那么您的应用程序很可能会崩溃。

为了确保实例在仍然需要时不会消失,ARC跟踪当前引用每个类实例的属性,常量和变量,只要至少对该实例的一个活动引用仍然存在,ARC将不会释放实例。

为了实现这一点,只要将类实例分配给属性(常量或者变量),该属性(常量或者变量)就可以强制引用该实例,这个应用被称为“强”引用,因为它保持了对该实例的坚定的约束,并且不允许它被接触分配,只要这种强烈的计数依然存在,

举例

1:引用计数为0或者不为0时的验证

首先新建一个Person类

import Foundation

class Person {

let name: String

init(name: String) {

self.name = name

print("Person类被实例化了")
}

deinit {

print("Person对象被销毁了")
}
}


去实例化这个类并且进行多次引用

import UIKit

class ViewController: UIViewController {

var reference1: Person?
var reference2: Person?
var reference3: Person?

override func viewDidLoad() {
super.viewDidLoad()

reference1 = Person(name: "lausen")
reference2 = reference1
reference3 = reference1

reference1 = nil
reference2 = nil
reference3 = nil

}
}


运行完成之后的打印情况



在以上的示例中,ARC能够跟踪Person创建的实例的引用数量,如果不再需要时,就给Person类的实例释放掉

2:两个类的实例之间的循环引用

新建一个Apartment类,Apartment类里面有一个Person类型的tenant属性,给上述的 Person类添加一个Apartment类型的apartment的属性,

Apartment类

import Foundation

class Apartment {

let unit: String

var tenant: Person?

init(unit: String) {

self.unit = unit
}

deinit {

print("Apartment对象被释放了")
}
}


Person类

import Foundation

class Person {

let name: String

var apartment: Apartment?

init(name: String) {

self.name = name

print("Person类被实例化了")
}

deinit {

print("Person对象被销毁了")
}
}


需要注意:Person类的apartment是可选的,Apartment的tenant属性也是可选的(即:是可以用weak来修饰的)

import UIKit

class ViewController: UIViewController {

var john: Person?
var unit4A: Apartment?

override func viewDidLoad() {
super.viewDidLoad()

john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")
}
}


在控制器里面声明了两个属性并且实例化之后,对实例的引用情况如下



当john有一个公寓,并且公寓有一个租客是john时,就会造成两个实例的循环引用

import UIKit

class ViewController: UIViewController {

var john: Person?
var unit4A: Apartment?

override func viewDidLoad() {
super.viewDidLoad()

john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")

//两个实例进行循环引用
john?.apartment = unit4A
unit4A?.tenant = john
}
}


如图:



这时候,Person实例对Apartment的实例有抢引用,Apartment的实例对Person的实例有一个强引用,因此,就算你断开了john和unit4A变量所持有的强引用时,引用计数也不会下降到0,并且实例不会被ARC释放内存

//即使赋值为nil,控制台也不会打印对象被释放的信息
john = nil
unit4A = nil


效果如图:



解决类实例之间的强引用循环

Swift提供了两种方法来解决强引用循环,若引用(weak)和未引用(unowned)

2.1 弱引用(weak)

一个弱引用不会对实例进行强引用,此行为可防止引用成为引用循环的一部分,在属性前面放置一个weak来声明一个若引用,因为一个弱引用并没有对它引用的实例保持强烈的控制,所以这个实例是可以被释放的,

在Apartment类里面的tenant属性前面加上weak关键字

//用weak修饰属性,对象就不会对该属性进行强引用
weak var tenant: Person?


在控制器里面注释掉两行代码

//即使赋值为nil,控制台也不会打印对象被释放的信息
//        john = nil
//        unit4A = nil


此时的引用情况如下



john = nil




打印结果:



unit4A = nil




打印结果:



此时:之前循环引用的对象就被全部释放了,但是,以上不管是Apartment的tenant属性还是Person的unit属性,都是可变的,也就是可以用weak来修饰的, 那如果属性不是可变的,该怎么办呢?—当当当当 未引用(unowned)登场

2.2 未引用(unowned)

想弱引用一样,一个无名的参考并不能保持对它所指的实例的强烈的控制,但是,与弱引用不同,当另一个实例具有相同的声明周期或者更长的声明周期时,将使用位置引用,您可以通过unowned关键字放在属性或变量声明之前来指示位置引用

例如:定义两个类,Customer(客户)以及CreditCard(信用卡)

客户可能没有信用卡,但是信用卡一定会关联一个客户,所以客户里面的信用卡类型的属性是可选的,但是信用卡里面的客户是必选的

类Customer

import Foundation

class Customer {

/// 消费者的名字
let name: String

/// 消费者手里的信用卡
var card: CreditCard?

init(name: String) {

self.name = name
}

deinit {
print("消费者的实例对象被释放了")
}
}


类CreditCard

import Foundation

class CreditCard{

let number: Int64

unowned let customer: Customer

init(num: Int64,customer: Customer) {

self.number = num

self.customer = customer
}

deinit {

print("信用卡的实例对象被释放了")
}
}


在控制器里实例化一个客户的对象,然后通过信用卡的实例化,给客户的信用卡赋值

import UIKit

class ViewController: UIViewController {

var john: Customer?

override func viewDidLoad() {
super.viewDidLoad()

john = Customer(name: "john Appleseed")
john?.card = CreditCard(num: 1234_5678_9012_3456, customer: john!)

}
}


相互引用如下



该Customer实例现在对CreditCard有强引用,并且该CreditCard实例对Customer的实例未引用(用了unowned关键字修饰了),所以当打破john变量所持有的强引用时,Customer就没有强引用了,所以如果Customer的实例会被释放,当Customer实例被释放了以后,CreditCard的实例也没有强引用了,也会被释放

//当变量john不引用Customer的实例的时候,Customer的实例就会被释放
john = nil


此时的引用情况如下:



运行结果如下:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  swift