ARC
ARC是swift使用的一种管理应用程序内存的机制,对于C语言我们知道,当我们申请一块空间,通常需要手动释放,不然会造成空间浪费,而有了ARC机制,你无需考虑内存的管理,因为ARC会在类的实例不再被使用时,自动释放内存空间。
ARC通常适用引用类型,比如类。
自动引用计数的规则:
- 每创建一个类的实例对象,ARC就分配一块内存存储实例信息,引用计数+1
- 当实例不再被使用,ARC自动释放实例所占内存,引用计数-1
- 当引用计数为0时,实例被销毁。
类实例之间的循环强引用
循环强引用:两个类实例都持有一个强引用的指向对方的属性
解决循环强引用方法:类之间的关系使用弱引用代替强引用。
循环强引用示例:
class A{
let aStr:String
var b:B?
init(a: String) {
self.aStr = a
}
deinit{
print("A's deinit")
}
}
class B{
var bStr:String
var a:A?
init(str:String){
self.bStr = str
}
deinit {
print("B's deinit")
}
}
var objA:A?
var objB:B?
objA = A(a: "AAAA")
objB = B(str: "BBBB")
objA!.b = objB
objB!.a = objA
objA = nil
objB = nil
//由于objA.b还指向B,objcB.a还指向A所以两者的实例还未被释放,此时打印无结果
此时如果要释放A和B只能这么做:
一般解决该办法之一是通过弱引用weak,弱引用不会增加ARC计数。
因此可以改成:
class A{
let aStr:String
weak var b:B?//使用弱引用
init(a: String) {
self.aStr = a
}
deinit{
print("A's deinit")
}
}
class B{
var bStr:String
weak var a:A?//使用弱引用
init(str:String){
self.bStr = str
}
deinit {
print("B's deinit")
}
}
var objA:A?
var objB:B?
objA = A(a: "AAAA")
objB = B(str: "BBBB")
objA!.b = objB
objB!.a = objA
objA = nil//此时A释放
objB = nil//此时B释放
无主引用Unowned
解决循环引用的另一种方式就是无主引用,无主引用修饰的实例属性与引用它的实例有着相同的生命周期
- 在声明属性或者变量时,在前面加上关键字
unowned
表示这是一个无主引用 - 使用无主引用,必须确保引用始终指向一个未销毁的实例,这也意味着无主引用的对象有确定的值。
- 如果试图在实例被销毁后,访问该实例的无主引用,会触发运行时错
class A{
let aStr:String
var b:B?//使用弱引用
init(a: String) {
self.aStr = a
}
deinit{
print("A's deinit")
}
}
class B{
var bStr:String
unowned var a:A?//使用弱引用
init(bStr: String, a: A? = nil) {
self.bStr = bStr
self.a = a
}
deinit {
print("B's deinit")
}
}
var objA:A?
objA = A(a: "AAAA")
objA!.b = B(bStr: "bbbb",a:objA)
objA = nil
//这里会释放A和B,因为B里的a是无主引用,类似于弱引用,这样就没有指向A的对象了,A被释放,A里面的b也被销毁,指向B的对象也没有了,B被释放
闭包引起的循环强引用
将一个闭包赋值给类实例的某个属性,并且这个闭包体中又使用了这个类实例时。这个闭包体中可能访问了实例的某个属性,或者闭包中调用了实例的某个方法,这两种情况都导致了闭包“捕获”self,从而产生了循环强引用。
例如:
class A{
let aStr:String
let isShow:Bool
lazy var closures:()->String = {
if self.isShow {
return self.aStr
}else{
return "isShow is False"
}
}
init(aStr: String, isShow: Bool) {
self.aStr = aStr
self.isShow = isShow
}
deinit{
print("A's deinit")
}
}
var objA:A?
objA = A(aStr: "AAAA", isShow: true)
var value:String = objA!.closures()
print(value)
objA = nil
解决办法跟类实例循环引用方法一样,声明每一个捕获引用为弱引用或者无主引用。
- 弱引用:在被捕获的引用可能会变为nil时,将闭包内的捕获定义为弱引用
- 无主引用 :在闭包和捕获的实例总是互相引用并且总是同时销毁时,将闭包内的捕获定义为无主引用
- 如果被捕获的引用绝对不会变为nil,应该用无主引用,而不是弱引用
示例:
class A{
let aStr:String
let isShow:Bool
lazy var closures:()->String = {
//捕获列表是[unowned self],表示将self捕获为无主引用而不是强引用
[unowned self] in
if self!.isShow {
return self!.aStr
}else{
return "isShow is False"
}
}
init(aStr: String, isShow: Bool) {
self.aStr = aStr
self.isShow = isShow
}
deinit{
print("A's deinit")
}
}
var objA:A?
objA = A(aStr: "AAAA", isShow: true)
var value:String = objA!.closures()
print(value)
objA = nil
//这里会释放A