-
11. 閉包表達式(Closure Expression) :
一種函數(shù)的定義方式
- 在Swift中,可以通過func定義一個函數(shù)锭亏,也可以通過閉包表達式定義一個函數(shù)
func sum(_ v1: Int, _ v2: Int) -> Int { v1 + v2 }
var fn = {
(v1: Int, v2: Int) -> Int in
return v1 + v2
}
fn(10, 20)
{
(v1: Int, v2: Int) -> Int in
return v1 + v2
}(10, 20)
{
(參數(shù)列表) -> 返回值類型 in 函數(shù)體代碼
}
-
12. 閉包
- 一個函數(shù)和它所捕獲的變量\常量環(huán)境組合起來纠吴,稱為閉包
- 一般指定義在函數(shù)內(nèi)部的函數(shù)
- 一般它捕獲的是外層函數(shù)的局部變量\常量
- 一個函數(shù)和它所捕獲的變量\常量環(huán)境組合起來纠吴,稱為閉包
typealias Fn = (Int) -> Int
func getFn() -> Fn {
var num = 0
func plus(_ i: Int) -> Int {
num += i
return num
}
return plus
} // 返回的plus和num形成了閉包
輔助理解:
- 可以把閉包想象成是一個類的實例對象
- 內(nèi)存在堆空間
- 捕獲的局部變量\常量就是對象的成員(存儲屬性)
- 組成閉包的函數(shù)就是類內(nèi)部定義的方法
class Closure {
var num = 0
func plus(_ i: Int) -> Int { num += i
return num
}
}
-
13. 自動閉包
@autoclosure
// 如果第1個數(shù)大于0,返回第一個數(shù)慧瘤。否則返回第2個數(shù)
func getFirstPositive(_ v1: Int, _ v2: Int) -> Int {
return v1 > 0 ? v1 : v2
}
getFirstPositive(10, 20) // 10
getFirstPositive(-2, 20) // 20
getFirstPositive(0, -4) // -4
// 改成函數(shù)類型的參數(shù)戴已,可以讓v2延遲加載
func getFirstPositive(_ v1: Int, _ v2: () -> Int) -> Int? {
return v1 > 0 ? v1 : v2()
}
getFirstPositive(-4) { 20 }
func getFirstPositive(_ v1: Int, _ v2: @autoclosure () -> Int) -> Int? {
return v1 > 0 ? v1 : v2()
}
getFirstPositive(-4, 20)
- @autoclosure 會自動將 20 封裝成閉包 { 20 }
- @autoclosure 只支持 () -> T 格式的參數(shù)
- @autoclosure 并非只支持最后1個參數(shù)
- 空合并運算符 ?? 使用了 @autoclosure 技術
- 有@autoclosure、無@autoclosure锅减,構成了函數(shù)重載
為了避免與期望沖突溪猿,使用了@autoclosure的地方最好明確注釋清楚:這個值會被推遲執(zhí)行
-
14. 屬性
-
存儲屬性(Stored Property)
- 類似于成員變量這個概念
- 存儲在實例的內(nèi)存中
- 結構體卸夕、類可以定義存儲屬性
- 枚舉不可以定義存儲屬性
- 在創(chuàng)建類 或 結構體的實例時掠廓,必須為所有的存儲屬性設置一個合適的初始值
- 可以在初始化器里為存儲屬性設置一個初始值
- 可以分配一個默認的屬性值作為屬性定義的一部分
-
延遲存儲屬性(Lazy Stored Property):
- 使用lazy可以定義一個延遲存儲屬性亏狰,在第一次用到屬性的時候才會進行初始化
- lazy屬性必須是var桦沉,不能是let(存儲類型屬性除外)
- let必須在實例的初始化方法完成之前就擁有值
- 如果多條線程同時第一次訪問lazy屬性
無法保證屬性只被初始化1次
- 當結構體包含一個延遲存儲屬性時,只有var才能訪問延遲存儲屬性, 因為延遲屬性初始化時需要改變結構體的內(nèi)存
-
計算屬性(Computed Property)
- 本質(zhì)就是方法(函數(shù))
- 不占用實例的內(nèi)存
- 枚舉金闽、結構體纯露、類都可以定義計算屬性
- set傳入的新值默認叫做newValue,也可以自定義
- 只讀計算屬性:只有get代芜,沒有set(有set埠褪,必須有get)
- 定義計算屬性只能用var,不能用let挤庇。let代表常量:值是一成不變的 钞速,而計算屬性的值是可能發(fā)生變化的(即使是只讀計算屬性)
枚舉原始值rawValue的本質(zhì)是:只讀計算屬性
-
類型屬性(Type Property):
- 屬性可以分為:
-
實例屬性(Instance Property):只能通過實例去訪問
- 存儲實例屬性(Stored Instance Property):存儲在實例的內(nèi)存中,每個實例都有1份
- 計算實例屬性(Computed Instance Property)
-
類型屬性(Type Property):只能通過類型去訪問
- 存儲類型屬性(Stored Type Property):整個程序運行過程中嫡秕,就只有1份內(nèi)存(類似于全局變量)
- 計算類型屬性(Computed Type Property)
- 可以通過static定義類型屬性
- 如果是類渴语,也可以用關鍵字class
-
ps:類型屬性細節(jié)
- 不同于存儲實例屬性,你必須給存儲類型屬性設定初始值昆咽。 因為類型沒有像實例那樣的init初始化器來初始化存儲屬性
- 存儲類型屬性默認就是lazy驾凶,會在第一次使用的時候才初始化。 就算被多個線程同時訪問潮改,
保證只會初始化一次
狭郑。 存儲類型屬性可以是let - 枚舉類型也可以定義類型屬性(存儲類型屬性腹暖、計算類型屬性)
-
- 屬性可以分為:
-
// 單例模式
public class FileManager {
public static let shared = FileManager()
private init() { }
}
public class FileManager {
public static let shared = {
// ....
// ....
return FileManager()
}()
private init() { }
}
-
15. 屬性觀察器(Property Observer)
- 可以為非lazy的var存儲屬性設置屬性觀察器
- willSet會傳遞新值汇在,默認叫newValu
- didSet會傳遞舊值,默認叫oldValue
- 在初始化器中設置屬性值不會觸發(fā)willSet和didSet
- 在屬性定義時設置初始值也不會觸發(fā)willSet和didSet
- 父類的屬性在它自己的初始化器中賦值不會觸發(fā)屬性觀察器脏答,但在子類的初始化器中賦值會觸發(fā)屬性觀察器
- 屬性觀察器糕殉、計算屬性的功能,同樣可以應用在全局變量殖告、局部變量身上
struct Circle {
var radius: Double {
willSet {
print("willSet", newValue)
}
didSet {
print("didSet", oldValue, radius)
}
}
init() {
self.radius = 1.0
print("Circle init!")
}
}
-
16. inout的本質(zhì)
- 如果實參有物理內(nèi)存地址阿蝶,且沒有設置屬性觀察器
- 直接將實參的內(nèi)存地址傳入函數(shù)(實參進行引用傳遞)
- 如果實參是計算屬性 或者 設置了屬性觀察器
- 采取了Copy In Copy Out的做法
- 調(diào)用該函數(shù)時,先復制實參的值黄绩,產(chǎn)生副本【get】
- 將副本的內(nèi)存地址傳入函數(shù)(副本進行引用傳遞)羡洁,在函數(shù)內(nèi)部可以修改副本的值
- 函數(shù)返回后,再將副本的值覆蓋實參的值【set】
- 總結:inout的本質(zhì)就是引用傳遞(地址傳遞)
- 如果實參有物理內(nèi)存地址阿蝶,且沒有設置屬性觀察器
-
17. 方法
-
mutating
結構體和枚舉是值類型爽丹,默認情況下筑煮,值類型的屬性不能被自身的實例方法修改。
在func關鍵字前加mutating可以允許這種修改行為 -
@discardableResult
在func前面加個@discardableResult粤蝎,可以消除:函數(shù)調(diào)用后返回值未被使用的警告
?
-
-
18. 下標(subscript)
- 使用subscript可以給任意類型(枚舉真仲、結構體、類)增加下標功能初澎,有些地方也翻譯為:下標腳本
- subscript的語法類似于實例方法秸应、計算屬性,本質(zhì)就是方法(函數(shù))
- subscript中定義的返回值類型決定了
get方法的返回值類型
set方法中newValue的類型 - subscript可以接受多個參數(shù),并且類型任意
- subscript可以沒有set方法软啼,但必須要有get方法桑谍。如果只有get方法,可以省略祸挪。
-
19.繼承(Inheritance)
- 值類型(枚舉霉囚、結構體)不支持繼承,只有類支持繼承匕积。
- 沒有父類的類盈罐,稱為:基類
- Swift并沒有像OC、Java那樣的規(guī)定:任何類最終都要繼承自某個基類
- 子類可以重寫父類的下標闪唆、方法盅粪、屬性,重寫必須加上override關鍵字
- 重寫類型方法悄蕾、下標:
1.被class修飾的類型方法票顾、下標,允許被子類重寫
2.被static修飾的類型方法帆调、下標奠骄,不允許被子類重寫 - 重寫屬性:
- 子類可以將父類的屬性(存儲、計算)重寫為
計算屬性
- 子類不可以將父類屬性重寫為存儲屬性
- 只能重寫var屬性番刊,不能重寫let屬性
- 重寫時含鳞,屬性名、類型要一致
- 子類重寫后的屬性權限 不能小于 父類屬性的權限
- 如果父類屬性是只讀的芹务,那么子類重寫后的屬性可以是只讀的蝉绷、也可以是可讀寫的
- 如果父類屬性是可讀寫的,那么子類重寫后的屬性也必須是可讀寫的
- 子類可以將父類的屬性(存儲、計算)重寫為
- 重寫類型屬性:
- 被class修飾的計算類型屬性枣抱,可以被子類重寫
- 被static修飾的類型屬性(存儲熔吗、計算),不可以被子類重寫
- 屬性觀察器:
- 可以在子類中為父類屬性(除了只讀計算屬性佳晶、let屬性)增加屬性觀察器
-
final
:- 被final修飾的方法桅狠、下標、屬性轿秧,禁止被重寫
- 被final修飾的類中跌,禁止被繼承
-
多態(tài)(繼承)原理圖示: