一铺董、內(nèi)存管理
1巫击、跟OC一樣,Swift也是采取基于引用計(jì)數(shù)的ARC內(nèi)存管理方案(針對堆空間)
2精续、Swift的ARC中有三種引用
- 強(qiáng)引用:默認(rèn)情況下坝锰,引用都是強(qiáng)引用。
- 弱引用:通過weak定義弱引用重付。
a.必須是可選類型的var顷级,因?yàn)閷?shí)例銷毀后,ARC會(huì)自動(dòng)將弱引用設(shè)置為nil确垫。
b.ARC自動(dòng)給弱引用設(shè)置nil時(shí)弓颈,不會(huì)觸發(fā)屬性觀察器。
- 無主引用:通過unowned定義無主引用删掀。
a.不會(huì)產(chǎn)生強(qiáng)引用翔冀,實(shí)例銷毀后仍然存儲(chǔ)著實(shí)例的內(nèi)存地址(類似于OC中的unsafe_unretained)
b.試圖在實(shí)例銷毀后訪問無主引用,會(huì)產(chǎn)生運(yùn)行時(shí)錯(cuò)誤(野指針)
- weak披泪、unowned的使用限制
weak纤子、unowned只能用在類的實(shí)例上面。
class PersonX { }
weak var person0 : PersonX?
weak var person1 : AnyObject?
unowned var person3 : PersonX?
unowned var person4 : AnyObject?
二、Autoreleasepool
我們可以看出控硼,Swift和OC的autoreleasepool使用沒什么不同跌捆。
for _ in 0...5000 {
autoreleasepool {
}
}
三、循環(huán)引用
1象颖、weak、unowned都能解決循環(huán)引用的問題姆钉,unowned要比weak少些性能消耗说订。
a.生命周期中可能會(huì)變?yōu)閚il的使用weak。
b.初始化賦值后再也不會(huì)變?yōu)閚il的使用unowned潮瓶。
c.這里有疑問陶冷,等到后面補(bǔ)充一下:
四、閉包循環(huán)引用
1毯辅、 閉包表達(dá)式默認(rèn)會(huì)對用到的外層對象產(chǎn)生額外的強(qiáng)應(yīng)用埂伦。(對外層對象進(jìn)行了retain操作)
- 下面代碼會(huì)產(chǎn)生循環(huán)引用,導(dǎo)致Person無法釋放思恐。(看不到Person的deinit被調(diào)用)
class PersonY {
var fn: (() -> ())?
func run() {
print("run")
}
deinit { print("deinit") }
}
錯(cuò)誤調(diào)用:
func test() {
let p = PersonY()
p.fn = {
p.run()
}
}
test()
修正方法:
func test() {
let p = PersonY()
p.fn = { [weak p] in
//或者[unowned p] in
p!.run()
}
}
test()
2沾谜、如果想在定義閉包屬性的同時(shí)引用self,這個(gè)閉包必須是lazy的(因?yàn)樵趯?shí)例方法初始化完畢之后才能引用self)
- 下面的閉包fn(Method)內(nèi)部如果用到了實(shí)例成員(屬性胀莹、方法)基跑,編譯器會(huì)強(qiáng)制要求明確寫出self。如果不寫描焰,會(huì)有如下錯(cuò)誤:
Call to method 'run' in closure requires explicit 'self.' to make capture semantics explicit(在閉包中調(diào)用方法“run”需要顯式的“self”)
class PersonZ {
lazy var fn:(() -> ()) = {
[weak self] in
self?.run()
}
func run() {
print("run")
}
deinit {
print("deinit")
}
}
- 如果lazy屬性是閉包調(diào)用的結(jié)果媳否,那么不用考慮循環(huán)引用的問題,因?yàn)殚]包調(diào)用后荆秦,閉包的生命周期就結(jié)束了篱竭。
class PersonM {
var age : Int = 0
lazy var getAge : Int = {
age
}()
deinit {
print("deinit")
}
}
五、@escaping
1步绸、非逃逸閉包掺逼、逃逸閉包,一般都是當(dāng)做參數(shù)傳遞給函數(shù)靡努。
- 非逃逸閉包:閉包調(diào)用在發(fā)生函數(shù)結(jié)束前坪圾,閉包調(diào)用在函數(shù)作用域內(nèi)。
- 逃逸閉包:閉包有可能在函數(shù)結(jié)束后調(diào)用惑朦,閉包調(diào)用逃離了函數(shù)的作用域兽泄,需要通過@escaping聲明。
a.首先聲明一個(gè)閉包類型:
typealias Fn = () -> ()
b.fn是非逃逸閉包
func test1(_ fn: Fn) {
fn()
}
c.fn是逃逸閉包
// fn是逃逸閉包
var gFn: Fn?
func test2(_ fn: @escaping Fn) {
gFn = fn
}
// fn是逃逸閉包
func test3(_ fn: @escaping Fn) {
DispatchQueue.global().async {
fn()
}
}
class Person {
var fn: Fn
// fn是逃逸閉包
init(fn: @escaping Fn) {
self.fn = fn
}
func run() {
// DispatchQueue.global().async也是一個(gè)逃逸閉包
// 它用到了實(shí)例成員(屬性漾月、方法)病梢,編譯器會(huì)強(qiáng)制要求明確寫出self DispatchQueue.global().async {
self.fn()
}
}
d.逃逸閉包注意點(diǎn):
逃逸閉包不可以捕獲inout參數(shù)
typealias Fn = () -> ()
func other1(_ fn: Fn) {
fn()
}
func other2(_ fn: @escaping Fn) {
fn()
}
func test(value: inout Int) -> Fn {
other1 { value += 1 }
// error: 逃逸閉包不能捕獲inout參數(shù)
other2 { value += 1 }
func plus() { value += 1 }
// error: 逃逸閉包不能捕獲inout參數(shù) return plus
}