Swift3.0 - 真的很簡(jiǎn)單
Swift3.0 - 數(shù)據(jù)類型
Swift3.0 - Array
Swift3.0 - 字典
Swift3.0 - 可選值
Swift3.0 - 集合
Swift3.0 - 流控制
Swift3.0 - 對(duì)象和類
Swift3.0 - 屬性
Swift3.0 - 函數(shù)和閉包
Swift3.0 - 初始化和釋放
Swift3.0 - 協(xié)議protocol
Swift3.0 - 類和結(jié)構(gòu)體的區(qū)別
Swift3.0 - 枚舉
Swift3.0 - 擴(kuò)展
Swift3.0 - 下標(biāo)
Swift3.0 - 泛型
Swift3.0 - 異常錯(cuò)誤
Swift3.0 - 斷言
Swift3.0 - 自動(dòng)引用計(jì)數(shù)(strong,weak,unowned)
Swift3.0 - 檢測(cè)API
Swift3.0 - 對(duì)象的標(biāo)識(shí)
Swift3.0 - 注釋
Swift3.0 - 元類型
Swift3.0 - 空間命名
Swift3.0 - 對(duì)象判等
Swift3.0 - 探究Self的用途
Swift3.0 - 類簇
Swift3.0 - 動(dòng)態(tài)調(diào)用對(duì)象(實(shí)例)方法
Swift3.0 - 文本輸出
Swift3.0 - 黑魔法swizzle
Swift3.0 - 鏡像
Swift3.0 - 遇到的坑
- 自動(dòng)引用計(jì)數(shù)的工作原理
1.每次創(chuàng)建一個(gè)類的新實(shí)例時(shí)饼记,都會(huì)分配一個(gè)內(nèi)存塊來存儲(chǔ)有關(guān)該實(shí)例的信息桦沉。此內(nèi)存保存實(shí)例的類型的信息,以及與該實(shí)例關(guān)聯(lián)的任何存儲(chǔ)屬性的值
2.當(dāng)一個(gè)實(shí)例不再需要時(shí)辙培,ARC釋放由該實(shí)例使用的內(nèi)存抒抬,以便內(nèi)存可以用于其他用途
3.ARC 釋放對(duì)象后,它將不能再繼續(xù)訪問對(duì)象的屬性,或者調(diào)用對(duì)象方法,如果你依然繼續(xù)訪問對(duì)象,App將會(huì)崩潰
4.為了確保對(duì)象使用時(shí), 不被釋放,ARC 跟蹤屬性,變量和常量,只要有一個(gè)對(duì)象引用存在,在對(duì)象不會(huì)被釋放
5.當(dāng)您將一個(gè)類實(shí)例分配給屬性、常量或變量時(shí)留荔,屬性喜最、常量或變量對(duì)實(shí)例會(huì)有一個(gè)強(qiáng)引用,確保對(duì)象不會(huì)被釋放
- 探討strong,weak和unowned 的區(qū)別
我們先創(chuàng)建一個(gè)對(duì)象
class Person{
var name:String
init(name:String) {
self.name = name
}
deinit {
print("對(duì)象釋放了,name的值為:\(name)")
}
}
創(chuàng)建一個(gè)Person實(shí)例對(duì)象,然后對(duì)其進(jìn)行強(qiáng)引用
var reference1:Person? = Person(name: "酷走天涯")
運(yùn)行結(jié)果:
沒有釋放對(duì)象,因?yàn)樗鼘?duì)象強(qiáng)引用
我們不再引用對(duì)象
reference1 = nil
運(yùn)行結(jié)果:
對(duì)象釋放了,name的值為:酷走天涯
Program ended with exit code: 0
我們使用weak引用對(duì)象
weak var reference1:Person? = Person(name: "酷走天涯")
運(yùn)行結(jié)果:
對(duì)象釋放了,name的值為:酷走天涯
Program ended with exit code: 0
我們修改代碼為
weak var reference1:Person = Person(name: "酷走天涯")
系統(tǒng)報(bào)錯(cuò):
weak 修飾的變量或者常量必須為可選值 ? 或者 !
使用unowned 引用對(duì)象
unowned var reference1:Person? = Person(name: "酷走天涯")
unowned var reference1:Person! = Person(name: "酷走天涯")
報(bào)錯(cuò):
不能修飾可選值類型的變量或者常量
修改代碼為
unowned var reference1:Person = Person(name: "酷走天涯")
運(yùn)行結(jié)果:
對(duì)象釋放了,name的值為:酷走天涯
Program ended with exit code: 0
階段總結(jié):
1.強(qiáng)引用的對(duì)象,不會(huì)被釋放
2.weak 或者unowned 引用的對(duì)象,當(dāng)沒有強(qiáng)引用的時(shí)候,會(huì)被立即釋放
3.weak 修飾的變量和常量必須為可選類型,但是unowned剛好和其相反必須為非可選類型
為了驗(yàn)證第三條結(jié)論,我們做下面的練習(xí)
var reference1:Person = Person(name: "酷走天涯")
weak var reference2 = reference1
unowned var reference3 = reference1
print(reference2)
print(reference3)
運(yùn)行結(jié)果:
Optional(swift3_0.Person)
swift3_0.Person
Program ended with exit code: 0
如果代碼為
var reference1:Person = Person(name: "酷走天涯")
weak var reference2 = reference1
unowned var reference3 = reference2
系統(tǒng)報(bào)錯(cuò),原因分析
weak 修飾的變量reference2 雖然沒有指明變量類型,但是swift會(huì)推斷出來它的類型為Person?,這個(gè)時(shí)候我們把一個(gè)person? 的類型付給unowned修飾的變量,系統(tǒng)不運(yùn)行
修改代碼為下面,系統(tǒng)編譯通過
var reference1:Person = Person(name: "酷走天涯")
weak var reference2 = reference1
unowned var reference3 = reference2!
- 為什么要使用weak 和unowned
定義兩個(gè)類Student 和School,Student 有一個(gè)屬性school ,school 也有一個(gè)屬性student,我們讓其相互引用
// 學(xué)生類
class Student{
var school:School?
deinit {
print("學(xué)生對(duì)象釋放")
}
}
// 學(xué)校類
class School{
var student:Student? // 強(qiáng)引用
deinit {
print("學(xué)校對(duì)象釋放了")
}
}
var school:School? = School()
var student:Student? = Student()
school!.student = student
student!.school = school
我們讓school 和 student 為nil
school = nil
student = nil
運(yùn)行代碼
發(fā)現(xiàn)兩個(gè)對(duì)象都沒有釋放
原因分析:
school 要釋放必須先釋放它的屬性Student ,系統(tǒng)就去釋放Student的內(nèi)存空間,發(fā)現(xiàn)他有一個(gè)屬性叫school 然后又去釋放school,就這樣構(gòu)成死循環(huán),誰都無法釋放
遇到上面的問題,原因就是相互強(qiáng)引用了,接下來,我們使用將student的屬性school 使用weak修飾
// 學(xué)生類
class Student{
weak var school:School?
deinit {
print("學(xué)生對(duì)象釋放")
}
}
運(yùn)行下邊的代碼
school = nil
student = nil
運(yùn)行結(jié)果:
學(xué)校對(duì)象釋放了
學(xué)生對(duì)象釋放
運(yùn)行原理分析:
首先我們釋放的school,沒有被弱引用,引用計(jì)數(shù)器減一,
你怎么選擇unowned和weak
先看下面的例子
使用unowned
// 學(xué)生類
class Student{
unowned var school:School
init(school:School) {
self.school = school
}
deinit {
print("學(xué)生對(duì)象釋放")
}
func describe() {
print("學(xué)生在\(school.describe())上學(xué)")
}
}
// 學(xué)校類
class School{
var student:Student? // 強(qiáng)引用
deinit {
print("學(xué)校對(duì)象釋放了")
}
func describe() ->String {
return "學(xué)校"
}
}
var school:School? = School()
var student:Student? = Student(school:school!)
school!.student = student
// 學(xué)校不用了,把學(xué)校釋放掉
school = nil
student?.describe()
運(yùn)行:
崩潰
原因:
釋放掉school對(duì)象,然后在student的方法中調(diào)用了school的方法,方法已經(jīng)不存在了,所以崩潰了
使用weak
// 學(xué)生類
class Student{
weak var school:School?
deinit {
print("學(xué)生對(duì)象釋放")
}
func describe() {
if let school = self.school{
print("學(xué)生在\(school.describe())上學(xué)")
}
}
}
// 學(xué)校類
class School{
var student:Student? // 強(qiáng)引用
deinit {
print("學(xué)校對(duì)象釋放了")
}
func describe() ->String {
return "學(xué)校"
}
}
var school:School? = School()
var student:Student? = Student()
school!.student = student
student!.school = school
運(yùn)行下面的代碼
// 學(xué)校不用了,把學(xué)校釋放掉
school = nil
student?.describe()
結(jié)果:
學(xué)校對(duì)象釋放了
分析:
由于school被弱引用,計(jì)數(shù)器減一,school對(duì)象就被釋放了,所以我們?cè)谡{(diào)用的時(shí)候進(jìn)行檢測(cè),如果對(duì)象存在再去執(zhí)行方法,這樣就避免了此類錯(cuò)誤
總結(jié):
使用unowned 修飾屬性時(shí),必須保證自己的實(shí)體獨(dú)享要比引用的對(duì)象先釋放
如果循環(huán)引用中,弱引用的對(duì)象必須為非可選類型,這個(gè)時(shí)候,就可以考慮使用unowned
- 實(shí)例分析
a.
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = {
if let text = self.text {
return "<\(self.name)>\(text)</\(self.name)>"
} else {
return "<\(self.name) />"
}
}
init(name: String, text: String? = nil) {
self.name = name
self.text = text
}
deinit {
print("\(name) is being deinitialized")
}
}
var x:HTMLElement? = HTMLElement(name: "title")
x = nil
運(yùn)行結(jié)果:
title is being deinitialized
Program ended with exit code: 0
分析:
眨眼一看,釋放了,為什么被釋放了,因?yàn)槲覀兪褂胠azy關(guān)鍵字,asHTML 閉包沒有被創(chuàng)建,這里注意,如果要在存儲(chǔ)屬性的閉包中訪問自己的屬性,必須加Lazy
那我們讓閉包代碼執(zhí)行以下
var x:HTMLElement? = HTMLElement(name: "title")
x?.asHTML()
x = nil
再次運(yùn)行:
驚奇的發(fā)現(xiàn),對(duì)象釋放不了
分析原因:
釋放對(duì)象,首先釋放屬性,釋放name發(fā)現(xiàn)name被閉包引用了,然后去釋放閉包,發(fā)現(xiàn)釋放self.name ,構(gòu)成了死循環(huán)
修改部分代碼如下
lazy var asHTML: () -> String = {
[unowned self] in
if let text = self.text {
return "<\(self.name)>\(text)</\(self.name)>"
} else {
return "<\(self.name) />"
}
}
運(yùn)行結(jié)果:
title is being deinitialized
Program ended with exit code: 0
分析:
釋放name的時(shí)候, 是被弱引用的,引用計(jì)數(shù)沒有加1,不用考慮,直接釋放自己料按,釋放asHTML 時(shí)發(fā)現(xiàn),name 已經(jīng)被釋放了,