Swift面向?qū)ο?/h1>
01-閉包的循環(huán)引用
- 循環(huán)引用形成條件
- 閉包中訪問了self或者self的屬性/方法
- 必須有一個強(qiáng)引用的指針指向函數(shù)的閉包參數(shù)
- 解決方案(三種)
- weak var weakSelf = self
- 在閉包的左花括號的右邊,參數(shù)類型的左邊 添加 [weak self],在閉包中訪問的self都是弱引用的,有可能為nil,所以self在閉包中是可選類型
- 在閉包的左花括號的右邊,參數(shù)類型的左邊 添加 [unowned self],在閉包中訪問的self都是弱引用的,不可能可能為nil,所以self在閉包中是必選選類型
02-弱引用weak 和 unowned的區(qū)別
- weak: 當(dāng)對象被系統(tǒng)回收時,地址會自動指向nil,為了防止野指針的問題
- unowned: 和OC中的__unsafe_unretained解決方式一致,當(dāng)對象被系統(tǒng)回收的時候內(nèi)存地址不會制動指向nil
func method2InSwift() {
//解決方式3: [unowned self]
//和OC中的__unsafe_unretained解決方式一致,當(dāng)對象被系統(tǒng)回收的時候內(nèi)存地址不會制動指向nil
loadData(userName: "蕾蕾") { [unowned self] (res) in
print("\(self)")
}
}
func method1InSwift() {
//解決方式2: [weak self]
//當(dāng)對象被系統(tǒng)回收時,地址會自動指向nil,為了防止野指針的問題
//和OC中 __weak解決方式的作用類似
//
loadData(userName: "蕾蕾") { [weak self] (res) in
print("\(self)")
}
}
func methodInOC() {
//解決方式1: 仿照OC的解決方式
weak var weakSelf = self
loadData(userName: "蕾蕾") { (res) in
print("\(weakSelf)")
}
}
03-構(gòu)造函數(shù)
- super.init()作用
- 對象的初始化時分段的,先初始化子類,確保子類的必選屬性有值,再調(diào)用super.init(),給父類中的必選屬性設(shè)置值
- 標(biāo)記對象已經(jīng)初始化完畢,對象初始化完畢之后,才能夠調(diào)用對象的方法
- override
- 函數(shù)名相同,參數(shù)的個數(shù),或者參數(shù)類型不同就形成了函數(shù)的重載
- 屬性和方法
- 構(gòu)造函數(shù)必須確保必選屬性一定有初始值
- 方法必須在對象構(gòu)造結(jié)束之后才可以調(diào)用
04-函數(shù)的重載
- 概念
- 函數(shù)名相同,參數(shù)的個數(shù),或者參數(shù)類型不同就形成了函數(shù)的重載,任意構(gòu)造函數(shù)都可以重載
- 簡化程序員的記憶,只需要記住一個函數(shù)名即可,是面向?qū)ο蟮闹匾卣髦?br>
OC沒有重載
- 構(gòu)造函數(shù)重載特殊之處
- 當(dāng)構(gòu)造函數(shù)發(fā)生了重載, 父類中沒有重寫的構(gòu)造函數(shù)都無法訪問
- 如果構(gòu)造函數(shù)發(fā)生了重載,如果依然能夠訪問父類中的沒有重寫的構(gòu)造函數(shù),就會造成必選屬性 num 無法設(shè)置初始值
//該函數(shù)是Student類中重載的構(gòu)造函數(shù)
let s = Student(num: "007")
//這兩個構(gòu)造函數(shù)是從Person中繼承的構(gòu)造函數(shù),并沒有實(shí)現(xiàn),此時是無法訪問的,編譯報錯
let s1 = Student()
let s2 = Student(name: "杜磊")
重載和重寫的對比,兩者之間沒有任何關(guān)系
- override
- 父類已經(jīng)存在的方法或者屬性,子類需要做特殊操作,在重寫的函數(shù)中可以super
- overload
- 函數(shù)名相同,參數(shù)的個數(shù),或者參數(shù)類型不同就形成了函數(shù)的重載
05-KVC構(gòu)造函數(shù)
- 基本數(shù)據(jù)類型使用注意
- 基本數(shù)據(jù)類型不能夠設(shè)置為可選類型
- KVC執(zhí)行流程
- 遍歷字典的鍵值對, 根據(jù)鍵 查找對象的屬性,就調(diào)用 setValue: forKey:
- 在 setValue: forKey:方法中判斷屬性是否存在
- 如果存在就直接根據(jù)鍵對應(yīng)的value設(shè)置值
- 如果不存在就調(diào)用setValue: forUndeinedKey: 空實(shí)現(xiàn)
init(dict: [String : Any]) {
//KVC通過字典給對象的屬性賦值
//KVC是一種間接的設(shè)置值的方式
super.init()
//遍歷字典的鍵值對, 根據(jù)鍵 查找對象的屬性
//就調(diào)用 setValue: forKey:
//在 setValue: forKey:方法中判斷屬性是否存在
//如果存在就直接根據(jù)鍵對應(yīng)的value設(shè)置值
//如果不存在就調(diào)用setValue: forUndeinedKey: 空實(shí)現(xiàn)
self.setValuesForKeys(dict)
}
override func setValue(_ value: Any?, forKey key: String) {
//OC中解決id的問題
// if ([key isEqualToStirng:@"id"]) {
// self.userId = value;
// //最好加上return
// return
// }
super.setValue(value, forKey: key)
}
//重寫
override func setValue(_ value: Any?, forUndefinedKey key: String) {
//如果super 依然會報錯
print(value,key)
}
06-其他構(gòu)造函數(shù)
可以返回nil
-
便利構(gòu)造函數(shù)
- 在本類中通過self調(diào)用指定的構(gòu)造函數(shù)
- 也可以在子類中通過self訪問父類中指定的構(gòu)造函數(shù)
-
OC中的便利構(gòu)造函數(shù)
- 在分類中增加構(gòu)造函數(shù)的時候不能夠使用super調(diào)用指定的構(gòu)造函數(shù),分類中沒有super
-
使用場景
- 在分類中增加構(gòu)造函數(shù)的時候需要使用到便利構(gòu)造函數(shù)
//對構(gòu)造條件進(jìn)行檢查的時候可以使用可以返回nil的構(gòu)造函數(shù)
convenience init?(name: String,age: Int) {
if age < 0 || age > 200 {
//條件不合法就不創(chuàng)建對象
return nil
}
//滿足條件,就根據(jù)傳遞過來的參數(shù)創(chuàng)建對象
//可以通過self來訪問本類中的指定的構(gòu)造函數(shù)
//也可以在子類中通過self訪問父類中指定的構(gòu)造函數(shù)
self.init(dict: ["name" : name, "age" : age])
}
//指定的構(gòu)造函數(shù)
init(dict: [String : Any]) {
super.init()
self.setValuesForKeys(dict)
}
07-懶加載
- 作用和意義
- 在使用的時候才會創(chuàng)建
- 保證在使用的時候一定不為nil
- 能夠讓代碼的格式更加規(guī)范
- 關(guān)鍵字
- lazy
- 兩種格式
//方式1: swift懶加載 lazy
lazy var nameLabel1: UILabel = UILabel()
//方式2
//需要在初始化的時候設(shè)置文字和文字顏色
//可以通過一個有返回值的閉包來完成label的初始化
lazy var nameLabel: UILabel = { //() -> UILabel in 只有在懶加載中才可以省略這部分
let l = UILabel()
return l
}()
08-get 和 set(newValue)
- get 和 set 是可以成對出現(xiàn),也可以只有g(shù)et,但是get一定不能夠省略
var _name: String?
var name: String? {
get {
print("調(diào)用get")
return _name
}
set {
//在set的代碼塊中能夠訪問newValue
_name = newValue
print("調(diào)用set")
}
}
09-didSet(oldValue) 和 willSet(newValue)
var name: String? {
//可以訪問 newValue
willSet {
print(newValue)
print("將要設(shè)置新值")
}
didSet {
//可以訪問到oldValue
print(oldValue)
//可以訪問到已經(jīng)設(shè)置完的值
print(name)
print("已經(jīng)設(shè)置完成")
}
}
10-只讀屬性/計算型屬性/readOnly
- 只實(shí)現(xiàn) get 代碼塊
//方式1
var title: String? {
get {
return "我的名字:\(name)"
}
}
//方式2,省略get代碼塊, 是方式1的簡寫方式
var title: String? {
return "我的名字:\(name)"
}
-
計算性屬性: 依賴其他屬性進(jìn)行計算
- 實(shí)現(xiàn)了 get 和 set
- 或者只實(shí)現(xiàn)了get
計算型屬性不占用內(nèi)存空間,每次執(zhí)行的時候都會調(diào)用,消耗cpu
懶加載只調(diào)用一次,以后都是讀取內(nèi)存,消耗內(nèi)存,效率更高
11-錯誤處理的三種方式
- try 默認(rèn)try 必須配合 do catch 來使用, 開發(fā)人員關(guān)注錯誤,并且需要對錯誤進(jìn)行處理
- try? 可選try 如果沒有錯誤返回可選類型的結(jié)果,如果有錯 就返回nil,
- try! 強(qiáng)行try 如果有錯 程序直接崩潰,如果沒喲錯誤返回的是必選類型的結(jié)果
/*
客戶端開發(fā)中很少使用默認(rèn)的try,會改變代碼的結(jié)構(gòu),在服務(wù)端開發(fā)中使用比較多
根據(jù)請求選中 try? 或者 try!
當(dāng)解析網(wǎng)絡(luò)請求的數(shù)據(jù)的時候可以使用 try?
當(dāng)使用本地的數(shù)據(jù)的時候 可以使用強(qiáng)行的try!
*/
//try
do {
let json = try JSONSerialization.jsonObject(with: jsonData!, options: [])
print(json)
} catch {
//可以捕捉錯誤 error只有在 catch對應(yīng)的代碼塊中才可以訪問
print(error)
}
//try?
let json = try? JSONSerialization.jsonObject(with: data!, options: [])
print(json)
//try!
let json = try! JSONSerialization.jsonObject(with: data!, options: [])
print(json)
12-項(xiàng)目
- 項(xiàng)目名稱一定不要使用中文和數(shù)組組合,否則出現(xiàn)數(shù)組中無法添加元素
- storyboard中使用自定義cell需要注意事項(xiàng)
tableView.dequeueReusableCell(withIdentifier: "DemoCellId", for: indexPath)
這種方式獲取可重用cell的前提是先注冊cell,storyboard中只需要設(shè)置可重用標(biāo)識符和關(guān)聯(lián)類就已經(jīng)完成了注冊,不需要手動注冊
(tableView.register(PersonCell.self , forCellReuseIdentifier: "DemoCellId")),
如果添加手動注冊,就屬于畫蛇添足的步驟了,會導(dǎo)致注冊的是自定義類,此時只會調(diào)用cell的默認(rèn)構(gòu)造函數(shù):
init(style: UITableViewCellStyle, reuseIdentifier: String?),會導(dǎo)致storyboard中添加的自定義控件都無法實(shí)例化
- as的使用
- as 默認(rèn)轉(zhuǎn)換
String(不是可選類型) -> NSString
[類型](不是可選類型) -> NSArray
[String : Any](不是可選類型) -> NSDictonary
- as! 強(qiáng)行轉(zhuǎn)換
- 強(qiáng)行將某一類型進(jìn)行轉(zhuǎn)換, 將父類類型的對象 轉(zhuǎn)換為子類類型的對象
- 如果轉(zhuǎn)換不成功 程序就boom
- 可以避免可選類型的問題
- as? 可選轉(zhuǎn)換
- 嘗試轉(zhuǎn)換 如果轉(zhuǎn)換失敗就為nil,如果轉(zhuǎn)換成功獲取結(jié)果是可選類型
- 比較安全一些
- 閉包中訪問了self或者self的屬性/方法
- 必須有一個強(qiáng)引用的指針指向函數(shù)的閉包參數(shù)
- weak var weakSelf = self
- 在閉包的左花括號的右邊,參數(shù)類型的左邊 添加 [weak self],在閉包中訪問的self都是弱引用的,有可能為nil,所以self在閉包中是可選類型
- 在閉包的左花括號的右邊,參數(shù)類型的左邊 添加 [unowned self],在閉包中訪問的self都是弱引用的,不可能可能為nil,所以self在閉包中是必選選類型
func method2InSwift() {
//解決方式3: [unowned self]
//和OC中的__unsafe_unretained解決方式一致,當(dāng)對象被系統(tǒng)回收的時候內(nèi)存地址不會制動指向nil
loadData(userName: "蕾蕾") { [unowned self] (res) in
print("\(self)")
}
}
func method1InSwift() {
//解決方式2: [weak self]
//當(dāng)對象被系統(tǒng)回收時,地址會自動指向nil,為了防止野指針的問題
//和OC中 __weak解決方式的作用類似
//
loadData(userName: "蕾蕾") { [weak self] (res) in
print("\(self)")
}
}
func methodInOC() {
//解決方式1: 仿照OC的解決方式
weak var weakSelf = self
loadData(userName: "蕾蕾") { (res) in
print("\(weakSelf)")
}
}
- 對象的初始化時分段的,先初始化子類,確保子類的必選屬性有值,再調(diào)用super.init(),給父類中的必選屬性設(shè)置值
- 標(biāo)記對象已經(jīng)初始化完畢,對象初始化完畢之后,才能夠調(diào)用對象的方法
- 函數(shù)名相同,參數(shù)的個數(shù),或者參數(shù)類型不同就形成了函數(shù)的重載
- 構(gòu)造函數(shù)必須確保必選屬性一定有初始值
- 方法必須在對象構(gòu)造結(jié)束之后才可以調(diào)用
- 函數(shù)名相同,參數(shù)的個數(shù),或者參數(shù)類型不同就形成了函數(shù)的重載,任意構(gòu)造函數(shù)都可以重載
- 簡化程序員的記憶,只需要記住一個函數(shù)名即可,是面向?qū)ο蟮闹匾卣髦?br> OC沒有重載
- 當(dāng)構(gòu)造函數(shù)發(fā)生了重載, 父類中沒有重寫的構(gòu)造函數(shù)都無法訪問
- 如果構(gòu)造函數(shù)發(fā)生了重載,如果依然能夠訪問父類中的沒有重寫的構(gòu)造函數(shù),就會造成必選屬性 num 無法設(shè)置初始值
//該函數(shù)是Student類中重載的構(gòu)造函數(shù)
let s = Student(num: "007")
//這兩個構(gòu)造函數(shù)是從Person中繼承的構(gòu)造函數(shù),并沒有實(shí)現(xiàn),此時是無法訪問的,編譯報錯
let s1 = Student()
let s2 = Student(name: "杜磊")
- 父類已經(jīng)存在的方法或者屬性,子類需要做特殊操作,在重寫的函數(shù)中可以super
- 函數(shù)名相同,參數(shù)的個數(shù),或者參數(shù)類型不同就形成了函數(shù)的重載
- 基本數(shù)據(jù)類型不能夠設(shè)置為可選類型
- 遍歷字典的鍵值對, 根據(jù)鍵 查找對象的屬性,就調(diào)用 setValue: forKey:
- 在 setValue: forKey:方法中判斷屬性是否存在
- 如果存在就直接根據(jù)鍵對應(yīng)的value設(shè)置值
- 如果不存在就調(diào)用setValue: forUndeinedKey: 空實(shí)現(xiàn)
init(dict: [String : Any]) {
//KVC通過字典給對象的屬性賦值
//KVC是一種間接的設(shè)置值的方式
super.init()
//遍歷字典的鍵值對, 根據(jù)鍵 查找對象的屬性
//就調(diào)用 setValue: forKey:
//在 setValue: forKey:方法中判斷屬性是否存在
//如果存在就直接根據(jù)鍵對應(yīng)的value設(shè)置值
//如果不存在就調(diào)用setValue: forUndeinedKey: 空實(shí)現(xiàn)
self.setValuesForKeys(dict)
}
override func setValue(_ value: Any?, forKey key: String) {
//OC中解決id的問題
// if ([key isEqualToStirng:@"id"]) {
// self.userId = value;
// //最好加上return
// return
// }
super.setValue(value, forKey: key)
}
//重寫
override func setValue(_ value: Any?, forUndefinedKey key: String) {
//如果super 依然會報錯
print(value,key)
}
可以返回nil
便利構(gòu)造函數(shù)
- 在本類中通過self調(diào)用指定的構(gòu)造函數(shù)
- 也可以在子類中通過self訪問父類中指定的構(gòu)造函數(shù)
OC中的便利構(gòu)造函數(shù)
- 在分類中增加構(gòu)造函數(shù)的時候不能夠使用super調(diào)用指定的構(gòu)造函數(shù),分類中沒有super
使用場景
- 在分類中增加構(gòu)造函數(shù)的時候需要使用到便利構(gòu)造函數(shù)
//對構(gòu)造條件進(jìn)行檢查的時候可以使用可以返回nil的構(gòu)造函數(shù)
convenience init?(name: String,age: Int) {
if age < 0 || age > 200 {
//條件不合法就不創(chuàng)建對象
return nil
}
//滿足條件,就根據(jù)傳遞過來的參數(shù)創(chuàng)建對象
//可以通過self來訪問本類中的指定的構(gòu)造函數(shù)
//也可以在子類中通過self訪問父類中指定的構(gòu)造函數(shù)
self.init(dict: ["name" : name, "age" : age])
}
//指定的構(gòu)造函數(shù)
init(dict: [String : Any]) {
super.init()
self.setValuesForKeys(dict)
}
- 在使用的時候才會創(chuàng)建
- 保證在使用的時候一定不為nil
- 能夠讓代碼的格式更加規(guī)范
- lazy
//方式1: swift懶加載 lazy
lazy var nameLabel1: UILabel = UILabel()
//方式2
//需要在初始化的時候設(shè)置文字和文字顏色
//可以通過一個有返回值的閉包來完成label的初始化
lazy var nameLabel: UILabel = { //() -> UILabel in 只有在懶加載中才可以省略這部分
let l = UILabel()
return l
}()
var _name: String?
var name: String? {
get {
print("調(diào)用get")
return _name
}
set {
//在set的代碼塊中能夠訪問newValue
_name = newValue
print("調(diào)用set")
}
}
var name: String? {
//可以訪問 newValue
willSet {
print(newValue)
print("將要設(shè)置新值")
}
didSet {
//可以訪問到oldValue
print(oldValue)
//可以訪問到已經(jīng)設(shè)置完的值
print(name)
print("已經(jīng)設(shè)置完成")
}
}
//方式1
var title: String? {
get {
return "我的名字:\(name)"
}
}
//方式2,省略get代碼塊, 是方式1的簡寫方式
var title: String? {
return "我的名字:\(name)"
}
計算性屬性: 依賴其他屬性進(jìn)行計算
- 實(shí)現(xiàn)了 get 和 set
- 或者只實(shí)現(xiàn)了get
計算型屬性不占用內(nèi)存空間,每次執(zhí)行的時候都會調(diào)用,消耗cpu
懶加載只調(diào)用一次,以后都是讀取內(nèi)存,消耗內(nèi)存,效率更高
/*
客戶端開發(fā)中很少使用默認(rèn)的try,會改變代碼的結(jié)構(gòu),在服務(wù)端開發(fā)中使用比較多
根據(jù)請求選中 try? 或者 try!
當(dāng)解析網(wǎng)絡(luò)請求的數(shù)據(jù)的時候可以使用 try?
當(dāng)使用本地的數(shù)據(jù)的時候 可以使用強(qiáng)行的try!
*/
//try
do {
let json = try JSONSerialization.jsonObject(with: jsonData!, options: [])
print(json)
} catch {
//可以捕捉錯誤 error只有在 catch對應(yīng)的代碼塊中才可以訪問
print(error)
}
//try?
let json = try? JSONSerialization.jsonObject(with: data!, options: [])
print(json)
//try!
let json = try! JSONSerialization.jsonObject(with: data!, options: [])
print(json)
tableView.dequeueReusableCell(withIdentifier: "DemoCellId", for: indexPath)
這種方式獲取可重用cell的前提是先注冊cell,storyboard中只需要設(shè)置可重用標(biāo)識符和關(guān)聯(lián)類就已經(jīng)完成了注冊,不需要手動注冊
(tableView.register(PersonCell.self , forCellReuseIdentifier: "DemoCellId")),
如果添加手動注冊,就屬于畫蛇添足的步驟了,會導(dǎo)致注冊的是自定義類,此時只會調(diào)用cell的默認(rèn)構(gòu)造函數(shù):
init(style: UITableViewCellStyle, reuseIdentifier: String?),會導(dǎo)致storyboard中添加的自定義控件都無法實(shí)例化
- as 默認(rèn)轉(zhuǎn)換
String(不是可選類型) -> NSString
[類型](不是可選類型) -> NSArray
[String : Any](不是可選類型) -> NSDictonary
- 強(qiáng)行將某一類型進(jìn)行轉(zhuǎn)換, 將父類類型的對象 轉(zhuǎn)換為子類類型的對象
- 如果轉(zhuǎn)換不成功 程序就boom
- 可以避免可選類型的問題
- 嘗試轉(zhuǎn)換 如果轉(zhuǎn)換失敗就為nil,如果轉(zhuǎn)換成功獲取結(jié)果是可選類型
- 比較安全一些