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