首先一张图解释一下循环引用
Swift问题解决方案
为了解决这个问题,swift提供了三种基本的解决方案,如下:
弱引用(Weak Reference)
如果被指向的实例有可能为nil,则使用弱引用
无主引用(Unowned Reference)
如果被指向的实例不为nil,则使用无主引用
捕获列表(Capture List)
如果在类属性使用闭包时,且闭包体内引用当前实例self而产生强引用环时,则使用捕获列表
下面对这三种方法和使用的情况分别做解释说明
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//强引用例子
strongReferenceMethod()
//解决方法一,弱引用例子,被指向的实例有可能为nil的时候
weakReferenceMethod()
//解决方法二,无主引用,被指向的实例不能为nil的时候
unownedReferenceMethod()
//解决方法三,捕获列表.如果在类属性使用闭包时,且闭包内引用当前实例self而产生强引用环时,则使用捕获列表
captureListMethod()
}
}
func strongReferenceMethod() {
var man:Male? = Male(name: "李雷")
var woman:Female? = Female(name: "韩梅梅")
man!.girlFriend = woman
woman!.boyFriend = man
debugPrint("韩妹妹和李雷完成了人生使命,我们告诉arc这两个实例不再使用了。")
debugPrint("woman 实例释放前,man.girlFriend 的值:\(man!.girlFriend)") //"woman 实例释放前,man.girlFriend 的值:Optional(StrongReferenceCycle.Female)"
woman = nil
debugPrint("woman 实例释放后,man.girlFriend 的值:\(man!.girlFriend)")
man = nil //"woman 实例释放后,man.girlFriend 的值:Optional(StrongReferenceCycle.Female)"
//可以看到并没有调用对应的dealloc销毁方法
}
func weakReferenceMethod() {
var man:Male2? = Male2(name: "李雷2")
var woman:Female2? = Female2(name: "韩梅梅2")
man!.girlFriend = woman
woman!.boyFriend = man
debugPrint("韩妹妹2和李雷2完成了人生使命,我们告诉arc这两个实例不再使用了。")
debugPrint("woman 实例释放前,man.girlFriend 的值:\(man!.girlFriend)")//"woman 实例释放前,man.girlFriend 的值:Optional(StrongReferenceCycle.Female2)"
debugPrint("man 实例释放前,woman.boyFriend 的值:\(woman!.boyFriend)")//"man 实例释放前,woman.boyFriend 的值:Optional(StrongReferenceCycle.Male2)"
woman = nil //"女人2实例被销毁"
debugPrint("woman 实例释放后,man.girlFriend 的值:\(man!.girlFriend)")//"woman 实例释放后,man.girlFriend 的值:nil"
man = nil //"男人2实例被销毁"
debugPrint("man 实例释放后,woman.boyFriend 的值:\(woman?.boyFriend )")//"man 实例释放后,woman.boyFriend 的值:nil"这里需要使用woman?来进行解包,否则因为woman已经是nil了,如果强制解包就会报错
//这里可以看到swift会主动保护弱引用的值。如man实例中的弱引用girlfriend属性,他指向的是woman属性,所以当woman实例释放,则girlfriend值会被swift重置为nil
}
func unownedReferenceMethod() {
//无主引用总是假定其引用对象一直存在(有值),因而无主引用不能修饰可选类型。使用场景1.其中一个类实例中国的属性为可选类型,即其值可以为nil,而另一个实例中的属性为非可选类型,即不能为nil。场景2.两个类实例中的属性,一旦初始化后,都不能为nil
var stark: Adult?
stark = Adult(name: "柰徳.斯塔克")
stark!.child = Child(name: "罗伯.斯塔克", guardian: stark!)
stark = nil
}
func captureListMethod() {
var weatherReport: WeatherReport? = WeatherReport(location: "成都", weather: "多云")
debugPrint("\(weatherReport!.reports())") //"成都的天气预报是:多云"
weatherReport = nil //但是这个执行后并没有调用dealloc方法释放
var weatherReport2: WeatherReport2? = WeatherReport2(location: "成都", weather: "多云")
debugPrint("\(weatherReport2!.reports())") //"成都的天气预报是:多云"
weatherReport2 = nil //"成都的天气预报实例被销毁!" 这里可以看到正确执行了dealloc方法
}
class Male {
let name: String
var girlFriend: Female? //这里互相引用
init(name: String) {
self.name = name
}
deinit {
debugPrint("男人实例被销毁")
}
}
class Female {
let name: String
var boyFriend: Male? //这里互相引用
init(name: String) {
self.name = name
}
deinit {
debugPrint("女人实例被销毁")
}
}
class Male2 {
let name: String
weak var girlFriend: Female2? //弱引用必须是变量,因为有可能在运行过程中值被修改了
init(name: String) {
self.name = name
}
deinit {
debugPrint("男人2实例被销毁")
}
}
class Female2 {
let name: String
weak var boyFriend: Male2? //弱引用必须是变量,因为有可能在运行过程中值被修改了
init(name: String) {
self.name = name
}
deinit {
debugPrint("女人2实例被销毁")
}
}
class Adult {
let name: String
init(name: String) {
self.name = name
}
var child: Child?
deinit {
debugPrint("adult\(name)被销毁!")
}
}
class Child {
let name: String
unowned var guardian:Adult //不能为nil的实例标注为unowned类型
init(name: String, guardian: Adult) {
self.name = name
self.guardian = guardian
}
deinit {
debugPrint("child\(name)被销毁!")
}
}
class WeatherReport {
let location: String
let weather: String
var temperature: Int?
//因为该计算属性使用到闭包且闭包中使用了self,所以必须是惰性属性.因为此时self和其他使用到的属性有可能还没有初始化完成就执行了reports属性内部的操作
lazy var reports: ()->String = {
if self.temperature != nil {
return "\(self.location)的天气预报是: \(self.weather), 气温是\(self.temperature)"
} else {
return "\(self.location)的天气预报是:\(self.weather)"
}
}
init(location: String, weather: String) {
self.location = location
self.weather = weather
}
deinit {
debugPrint("\(location)的天气预报实例被销毁!")
}
}
class WeatherReport2 {
let location: String
let weather: String
var temperature: Int?
//因为该计算属性使用到闭包且闭包中使用了self,所以必须是惰性属性.因为此时self和其他使用到的属性有可能还没有初始化完成就执行了reports属性内部的操作
lazy var reports: ()->String = {
[weak self] in //[weak self] in 和 [unowned self] in作用一样,区别见前面的weak和unowned的解释
if self!.temperature != nil {
return "\(self!.location)的天气预报是: \(self!.weather), 气温是\(self!.temperature)"
} else {
return "\(self!.location)的天气预报是:\(self!.weather)"
}
}
init(location: String, weather: String) {
self.location = location
self.weather = weather
}
deinit {
debugPrint("\(location)的天气预报实例被销毁!")
}
}