Swift 簡(jiǎn)介
查看Swift當(dāng)前版本
$ xcrun swift --version
簡(jiǎn)介
- Swift 語(yǔ)言由蘋果公司在 2014 年推出,用來撰寫 OS X 和 iOS 應(yīng)用程序
- 2014 年,在
Apple WWDC
發(fā)布 - 2016.3月
Apple WWDC
支持 Linux系統(tǒng),國(guó)外已經(jīng)有人開始用Swift 寫服務(wù)端
歷史
- 2010 年 7 月,蘋果開發(fā)者工具部門總監(jiān)
克里斯·拉特納
開始著手 Swift 編程語(yǔ)言的設(shè)計(jì) - 用一年時(shí)間居砖,完成基本架構(gòu)
- Swift 大約歷經(jīng) 4 年的開發(fā)期,2014 年 6 月發(fā)布
- 在 2015.12.3 開源, 現(xiàn)在GitHub上超過30000個(gè)星星
從發(fā)布至今,蘋果的每一個(gè)舉措都彰顯其大力推廣 Swift 的決心
版本
- 正式版 Swift 3.0, Xcode 8.0
Swift 特色
- Swfit3.0 是變化最大的一個(gè)版本,全面的去OC化了,Foundation框架的
NS
前綴去掉了 - 可以使用現(xiàn)有的
Cocoa
和Cocoa Touch
框架 - 蘋果宣稱 Swift的特點(diǎn)是:快速揩慕、現(xiàn)代、安全扮休、互動(dòng)迎卤,而且明顯優(yōu)于 Objective-C 語(yǔ)言
- 取消了預(yù)編譯指令包括宏
- Swift
取消了 Objective-C 的指針
及其他不安全訪問的使用 - 舍棄 Objective-C 早期應(yīng)用
Smalltalk
的語(yǔ)法,全面改為句點(diǎn)表示法 - 提供了類似 Java 的名字空間(
namespace
)玷坠、泛型(generic
)蜗搔、運(yùn)算對(duì)象重載(operator overloading
) - Swift 被簡(jiǎn)單的形容為 “沒有 C 的 Objective-C”(Objective-C without the C)
Swift 現(xiàn)狀
- 目前國(guó)內(nèi)有些公司的新項(xiàng)目已經(jīng)直接采用 Swift 開發(fā)
- 目前很多公司都在做 Swift 的人才儲(chǔ)備
- 應(yīng)聘時(shí),會(huì) Swift 開發(fā)無疑會(huì)增加自身籌碼
- 很多公司現(xiàn)在已經(jīng)深陷Swfit無法自拔了,包括唐巧的猿題庫(kù)
為什么要學(xué)習(xí) Swift八堡?
- 從4月份開始樟凄,蘋果提供的資料已經(jīng)沒有 OC 的了,這說明蘋果推動(dòng) Swift 的決心
- OC 源自于
smalltack-c
兄渺,迄今已經(jīng)有 40 多年的歷史缝龄,雖然OC
的項(xiàng)目還會(huì)在未來持續(xù)一段時(shí)間,但是更換成Swift
是未來必然的趨勢(shì) - 現(xiàn)在很多公司都注重人才儲(chǔ)備溶耘,如果會(huì)Swift二拐,就業(yè)會(huì)有很大的優(yōu)勢(shì),簡(jiǎn)歷中如果寫上會(huì) Swift凳兵,雖然面試中雖然不會(huì)怎么被問到百新,但對(duì)于薪資提升有很大幫助,同時(shí)可以從另外一個(gè)側(cè)面證明我們是有自學(xué)能力的人庐扫,這是所有企業(yè)都需要的
- Swift 里面融合了很多其他面向?qū)ο笳Z(yǔ)言的思想饭望,不像OC那么封閉仗哨,學(xué)會(huì) Swift,再轉(zhuǎn)其他語(yǔ)言會(huì)輕松很多
- Swift 畢竟也是出身自蘋果铅辞,整體程序開發(fā)思路和 OC 是一樣的厌漂,等 Swift 項(xiàng)目講完后,大家完全可以用同樣的思路寫出 OC 的來斟珊,而且在翻寫的過程中苇倡,能夠?qū)芏嘣竞雎缘?OC 基本功有很大的加強(qiáng)和改善
Swift 開發(fā)快速體驗(yàn)
目標(biāo)
- playground 快速體驗(yàn) & 學(xué)習(xí)資源分享
- 項(xiàng)目開發(fā)快速體驗(yàn),了解 Swift 基本程序結(jié)構(gòu)
學(xué)習(xí)資源
- 蘋果官方博客 https://developer.apple.com/swift/blog/
- 蘋果官方 Swift 2.0 電子書 https://itunes.apple.com/us/book/id1002622538
- 2.0 中文版 http://wiki.jikexueyuan.com/project/swift/
- 100個(gè)Swift必備tips囤踩,作者王巍旨椒,建議購(gòu)買實(shí)體書 http://onevcat.com
基本語(yǔ)法
目標(biāo)
- 熟悉 Swift 基本語(yǔ)法
- 常量 & 變量
- 可選項(xiàng)
- 控制流
- if
- 三目
- if let
- guard
- switch
- 字符串
- 循環(huán)
- 集合
- 數(shù)組
- 集合
變量和常量
需要掌握
定義
-
let
定義常量,一經(jīng)賦值不允許再修改 -
var
定義變量堵漱,賦值之后仍然可以修改
//: # 常量
//: 定義常量并且直接設(shè)置數(shù)值
let x = 20
//: 常量數(shù)值一經(jīng)設(shè)置综慎,不能修改,以下代碼會(huì)報(bào)錯(cuò)
// x = 30
//: 使用 `: 類型`勤庐,僅僅只定義類型示惊,而沒有設(shè)置數(shù)值
let x1: Int
//: 常量有一次設(shè)置數(shù)值的機(jī)會(huì),以下代碼沒有問題愉镰,因?yàn)?x1 還沒有被設(shè)置數(shù)值
x1 = 30
//: 一旦設(shè)置了數(shù)值之后米罚,則不能再次修改,以下代碼會(huì)報(bào)錯(cuò)岛杀,因?yàn)?x1 已經(jīng)被設(shè)置了數(shù)值
// x1 = 50
//: # 變量
//: 變量設(shè)置數(shù)值之后阔拳,可以繼續(xù)修改數(shù)值
var y = 200
y = 300
自動(dòng)推導(dǎo)
- Swift能夠根據(jù)右邊的代碼,推導(dǎo)出變量的準(zhǔn)確類型
- 通常在開發(fā)時(shí)类嗤,不需要指定變量的類型
- 如果要指定變量糊肠,可以在變量名后使用:,然后跟上變量的類型
重要技巧:Option + Click 可以查看變量的類型
[圖片上傳失敗...(image-1c3941-1536302339170)]
沒有隱式轉(zhuǎn)換R怕唷;豕!
- Swift 對(duì)數(shù)據(jù)類型要求異常嚴(yán)格
- 任何時(shí)候精偿,都不會(huì)做隱式轉(zhuǎn)換
如果要對(duì)不同類型的數(shù)據(jù)進(jìn)行計(jì)算弧圆,必須要顯式的轉(zhuǎn)換
let x2 = 100
let y2 = 10.5
let num1 = Double(x2) + y2
let num2 = x2 + Int(y2)
let & var 的選擇
-
應(yīng)該盡量先選擇常量,只有在必須修改時(shí)笔咽,才需要修改為
var
- 在 Xcode 7.0 中搔预,如果沒有修改變量,Xcode 會(huì)提示修改為
let
Optional 可選值
需要掌握
- 理解可選項(xiàng)的概念
- 理解可選項(xiàng)的概念 --> 要么有值,要么為nil
- 知道可選項(xiàng)的規(guī)則 --> 參與計(jì)算需要強(qiáng)制解包
- 知道兩個(gè)符號(hào)
- '?' 定義可選項(xiàng)
- '!' 對(duì)可選類型進(jìn)行強(qiáng)制解包, 程序員一定要對(duì)解包
負(fù)責(zé)
- 常量的可選項(xiàng)使用前需要設(shè)置初始值
- 變量的可選項(xiàng)默認(rèn)是nil
介紹
-
Optional
是 Swift 的一大特色叶组,也是 Swift 初學(xué)者最容易困惑的問題 - 定義變量時(shí)拯田,如果指定是
可選的
,表示該變量可以有一個(gè)指定類型的值甩十,也可以是 nil
- 定義變量時(shí)船庇,在類型后面添加一個(gè)
?
吭产,表示該變量是可選的 - 變量可選項(xiàng)的默認(rèn)值是
nil
- 常量可選項(xiàng)沒有默認(rèn)值,主要用于在構(gòu)造函數(shù)中給常量設(shè)置初始數(shù)值
//: num 可以是一個(gè)整數(shù)鸭轮,也可以是 nil臣淤,注意如果為 nil,不能參與計(jì)算
let num: Int? = 10
- 如果 Optional 值是
nil
窃爷,不允許參與計(jì)算 - 只有
解包(unwrap)
后才能參與計(jì)算 - 在變量后添加一個(gè)
!
邑蒋,可以強(qiáng)行解包
注意:必須要確保解包后的值不是 nil,否則會(huì)報(bào)錯(cuò)
//: num 可以是一個(gè)整數(shù)吞鸭,也可以是 nil寺董,注意如果為 nil覆糟,不能參與計(jì)算
let num: Int? = 10
//: 如果 num 為 nil刻剥,使用 `!` 強(qiáng)行解包會(huì)報(bào)錯(cuò)
let r1 = num! + 100
//: 使用以下判斷,當(dāng) num 為 nil 時(shí)滩字,if 分支中的代碼不會(huì)執(zhí)行
if let n = num {
let r = n + 10
}
常見錯(cuò)誤
unexpectedly found nil while unwrapping an Optional value
翻譯
在[解包]一個(gè)可選值時(shí)發(fā)現(xiàn) nil
??
運(yùn)算符
-
??
運(yùn)算符可以用于判斷變量/常量
的數(shù)值是否是nil
造虏,如果是則使用后面的值替代 - 在使用 Swift 開發(fā)時(shí),
??
能夠簡(jiǎn)化代碼的編寫
let num: Int? = nil
let r1 = (num ?? 0) + 10
print(r1)
控制流
if
- Swift 中沒有 C 語(yǔ)言中的
非零即真
概念 - 在邏輯判斷時(shí)必須顯示地指明具體的判斷條件
true
/false
- if 語(yǔ)句條件的
()
可以省略 - 但是
{}
不能省略
let num = 200
if num < 10 {
print("比 10 小")
} else if num > 100 {
print("比 100 大")
} else {
print("10 ~ 100 之間的數(shù)字")
}
三目運(yùn)算
- Swift 中的
三目
運(yùn)算保持了和 OC 一致的風(fēng)格
var a = 10
var b = 20
let c = a > b ? a : b
print(c)
適當(dāng)?shù)剡\(yùn)用三目麦箍,能夠讓代碼寫得更加簡(jiǎn)潔
可選項(xiàng)判斷
- 由于可選項(xiàng)的內(nèi)容可能為
nil
漓藕,而一旦為nil
則不允許參與計(jì)算 - 因此在實(shí)際開發(fā)中,經(jīng)常需要判斷可選項(xiàng)的內(nèi)容是否為
nil
單個(gè)可選項(xiàng)判斷
let url = NSURL(string: "http://www.baidu.com")
//: 方法1: 強(qiáng)行解包 - 缺陷挟裂,如果 url 為空享钞,運(yùn)行時(shí)會(huì)崩潰
let request = NSURLRequest(URL: url!)
//: 方法2: 首先判斷 - 代碼中仍然需要使用 `!` 強(qiáng)行解包
if url != nil {
let request = NSURLRequest(URL: url!)
}
可選項(xiàng)條件判斷
//: 1> 初學(xué) swift 一不小心就會(huì)讓 if 的嵌套層次很深,讓代碼變得很丑陋
if let u = url {
if u.host == "www.baidu.com" {
let request = NSURLRequest(URL: u)
}
}
//: 2> 使用 ',' 可以獲取前面賦值對(duì)象
if let u = url , u.host == "www.baidu.com" {
let request = NSURLRequest(URL: u)
}
- 小結(jié)
-
if let
不能與使用&&
诀蓉、||
等條件判斷
-
多個(gè)可選項(xiàng)判斷
//: 3> 可以使用 `,` 同時(shí)判斷多個(gè)可選項(xiàng)是否為空
let oName: String? = "張三"
let oNo: Int? = 100
if let name = oName {
if let no = oNo {
print("姓名:" + name + " 學(xué)號(hào): " + String(no))
}
}
if let name = oName, let no = oNo {
print("姓名:" + name + " 學(xué)號(hào): " + String(no))
}
判斷之后對(duì)變量需要修改
let oName: String? = "張三"
let oNum: Int? = 18
if var name = oName, num = oNum {
name = "李四"
num = 1
print(name, num)
}
guard
-
guard
是與if let
相反的語(yǔ)法栗竖,Swift 2.0 推出的
let oName: String? = "張三"
let oNum: Int? = 18
guard let name = oName else {
print("name 為空")
return
}
guard let num = oNum else {
print("num 為空")
return
}
// 代碼執(zhí)行至此,name & num 都是有值的
print(name)
print(num)
- 在程序編寫時(shí)渠啤,條件檢測(cè)之后的代碼相對(duì)是比較復(fù)雜的
- 使用 guard 的好處
- 能夠判斷每一個(gè)值
- 在真正的代碼邏輯部分狐肢,省略了一層嵌套
switch
-
switch
不再局限于整數(shù) -
switch
可以針對(duì)任意數(shù)據(jù)類型
進(jìn)行判斷 - 不再需要
break
- 每一個(gè)
case
后面必須有可以執(zhí)行的語(yǔ)句 - 要保證處理所有可能的情況,不然編譯器直接報(bào)錯(cuò)沥曹,不處理的條件可以放在
default
分支中 - 每一個(gè)
case
中定義的變量?jī)H在當(dāng)前case
中有效份名,而 OC 中需要使用{}
let score = "優(yōu)"
switch score {
case "優(yōu)":
let name = "學(xué)生"
print(name + "80~100分")
case "良": print("70~80分")
case "中": print("60~70分")
case "差": print("不及格")
default: break
}
for 循環(huán)
- OC 風(fēng)格的循環(huán) '++'語(yǔ)法已經(jīng)廢棄
var sum = 0
for var i = 0; i < 10; i++ {
sum += I
}
print(sum)
-
for-in
,0..<10 表示從0到9
sum = 0
for i in 0..<10 {
sum += I
}
print(sum)
- 范圍 0...10 表示從0到10
sum = 0
for i in 0...10 {
sum += I
}
print(sum)
- 省略下標(biāo)
-
_
能夠匹配任意類型 -
_
表示忽略對(duì)應(yīng)位置的值
-
for _ in 0...10 {
print("hello")
}
字符串
在 Swift 中絕大多數(shù)的情況下妓美,推薦使用
String
類型
- String 是一個(gè)結(jié)構(gòu)體僵腺,性能更高
- String 目前具有了絕大多數(shù) NSString 的功能
- String 支持直接遍歷
- NSString 是一個(gè) OC 對(duì)象,性能略差
- Swift 提供了
String
和NSString
之間的無縫轉(zhuǎn)換
字符串演練
- 遍歷字符串中的字符
for s in str.characters {
print(s)
}
- 字符串長(zhǎng)度
// 返回以字節(jié)為單位的字符串長(zhǎng)度壶栋,一個(gè)中文占 3 個(gè)字節(jié)
let len1 = str.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)
// 返回實(shí)際字符的個(gè)數(shù)
let len2 = str.characters.count
- 字符串拼接
- 直接在 "" 中使用
\(變量名)
的方式可以快速拼接字符串
- 直接在 "" 中使用
let str1 = "Hello"
let str2 = "World"
let i = 32
str = "\(i) 個(gè) " + str1 + " " + str2
我和我的小伙伴再也不要考慮
stringWithFormat
了 :D
- 可選項(xiàng)的拼接
- 如果變量是可選項(xiàng)辰如,拼接的結(jié)果中會(huì)有
Optional
- 為了應(yīng)對(duì)強(qiáng)行解包存在的風(fēng)險(xiǎn),蘋果提供了
??
操作符 -
??
操作符用于檢測(cè)可選項(xiàng)是否為nil
- 如果不是
nil
委刘,使用當(dāng)前值 - 如果是
nil
丧没,使用后面的值替代
- 如果不是
- 如果變量是可選項(xiàng)辰如,拼接的結(jié)果中會(huì)有
let str1 = "Hello"
let str2 = "World"
let i: Int? = 32
str = "\(i ?? 0) 個(gè) " + str1 + " " + str2
- 格式化字符串
- 在實(shí)際開發(fā)中鹰椒,如果需要指定字符串格式,可以使用
String(format:...)
的方式
- 在實(shí)際開發(fā)中鹰椒,如果需要指定字符串格式,可以使用
let h = 8
let m = 23
let s = 9
let timeString = String(format: "%02d:%02d:%02d", arguments: [h, m, s])
let timeStr = String(format: "%02d:%02d:%02d", h, m, s)
String & Range 的結(jié)合
- 在 Swift 中呕童,
String
和Range
連用時(shí)漆际,語(yǔ)法結(jié)構(gòu)比較復(fù)雜 - 如果不習(xí)慣 Swift 的語(yǔ)法,可以將字符串轉(zhuǎn)換成
NSString
再處理
let helloString = "我們一起飛"
(helloString as NSString).substringWithRange(NSMakeRange(2, 3))
- 使用 Range<Index> 的寫法 前面不要試圖去掌握這段代碼,每一個(gè)版本都在變化,沒用
let str = "愛笑的人運(yùn)氣不會(huì)太差喲"
let startIndex = str.index(str.startIndex, offsetBy: 2)
let endIndex = str.index(str.endIndex, offsetBy: -1)
let subStr1 = str.substring(to: startIndex)
print(subStr1)
let subStr2 = str.substring(from: endIndex)
print(subStr2)
let subStr3 = str.substring(with: startIndex..<endIndex)
print(subStr3)
//String 和 NSString 之間可以相互轉(zhuǎn)換 可以轉(zhuǎn)換為 NSString 來截取子串
let NStr = (str as NSString).substring(with: NSRange(location: 1, length: 2))
print(NStr)
集合
數(shù)組
- 數(shù)組使用
[]
定義夺饲,這一點(diǎn)與 OC 相同
//: [Int]
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
- 遍歷
for num in numbers {
print(num)
}
- 通過下標(biāo)獲取指定項(xiàng)內(nèi)容
let num1 = numbers[0]
let num2 = numbers[1]
- 可變&不可變
-
let
定義不可變數(shù)組 -
var
定義可變數(shù)組
-
let array = ["zhangsan", "lisi"]
//: 不能向不可變數(shù)組中追加內(nèi)容
//array.append("wangwu")
var array1 = ["zhangsan", "lisi"]
//: 向可變數(shù)組中追加內(nèi)容
array1.append("wangwu")
- 數(shù)組的類型
- Swift中數(shù)組建議存放相同類型的元素
- 如果初始化時(shí)奸汇,所有內(nèi)容類型不一致,需要手動(dòng)將數(shù)組的類型轉(zhuǎn)換為[Any]
//: array1 僅允許追加 String 類型的值
//array1.append(18)
var array2: [Any] = ["zhangsan", 18]
//: 在 Swift 中往声,數(shù)字可以直接添加到集合擂找,不需要再轉(zhuǎn)換成 `NSNumber`
array2.append(100)
- 數(shù)組的定義和實(shí)例化
- 使用
:
可以只定義數(shù)組的類型 - 實(shí)例化之前不允許添加值
- 使用
[類型]()
可以實(shí)例化一個(gè)空的數(shù)組
- 使用
var array3: [String]
//: 實(shí)例化之前不允許添加值
//array3.append("laowang")
//: 實(shí)例化一個(gè)空的數(shù)組
array3 = [String]()
array3.append("laowang")
- 數(shù)組的合并
- 必須是相同類型的數(shù)組才能夠合并
- 開發(fā)中,通常數(shù)組中保存的對(duì)象類型都是一樣的浩销!
array3 += array1
//: 必須是相同類型的數(shù)組才能夠合并贯涎,以下兩句代碼都是不允許的
//array3 += array2
//array2 += array3
- 數(shù)組的刪除
//: 刪除指定位置的元素
array3.removeAtIndex(3)
//: 清空數(shù)組
array3.removeAll()
字典
- 定義
- 同樣使用
[]
定義字典 -
let
不可變字典 -
var
可變字典 -
[String : Any]
是最常用的字典類型
- 同樣使用
//: [String : Any] 是最常用的字典類型
var dict = ["name": "zhangsan", "age": 18]
- 賦值
- 賦值直接使用
dict[key] = value
格式 - 如果 key 不存在,會(huì)設(shè)置新值
- 如果 key 存在慢洋,會(huì)覆蓋現(xiàn)有值
- 賦值直接使用
//: * 如果 key 不存在塘雳,會(huì)設(shè)置新值
dict["title"] = "boss"
//: * 如果 key 存在,會(huì)覆蓋現(xiàn)有值
dict["name"] = "lisi"
dict
- 遍歷
-
k
普筹,v
可以隨便寫 - 前面的是
key
- 后面的是
value
-
//: 遍歷
for (k, v) in dict {
print("\(k) ~~~ \(v)")
}
- 合并字典
- 如果 key 不存在败明,會(huì)建立新值,否則會(huì)覆蓋現(xiàn)有值
//: 合并字典
var dict1 = [String: NSObject]()
dict1["nickname"] = "大老虎"
dict1["age"] = 100
//: 如果 key 不存在太防,會(huì)建立新值妻顶,否則會(huì)覆蓋現(xiàn)有值
for (k, v) in dict1 {
dict[k] = v
}
print(dict)
錯(cuò)誤處理
需要了解
- 知道錯(cuò)誤處理機(jī)制的三種方式
- 開發(fā)中通常使用第二種 try?
- 在 Swfit 3.0 中 Foundation 的部分類去除了
NS
前綴
代碼演練,網(wǎng)絡(luò)訪問反序列化數(shù)據(jù)
- 網(wǎng)絡(luò)訪問代碼
let url = URL(string: "http://www.weather.com.cn/data/sk/101010100.html")!
URLSession.shared.dataTask(with: url, completionHandler: { (data, _, error) in
if error != nil {
print(error)
return
}
}).resume()
- JSON反序列化
// 方式1:強(qiáng)try.加載失敗直接崩潰
let dict = try! JSONSerialization.jsonObject(with: data!, options: [])
print(dict)
// 方式2:可選try.反序列化失敗返回nil
let dict = try? JSONSerialization.jsonObject(with: data!, options: [])
print(dict)
// 方式3:默認(rèn)try.返回序列化失敗可以捕捉詳細(xì)信息
do {
let dict = try JSONSerialization.jsonObject(with: data!, options: [])
print(dict)
}catch {
print(error)
}
函數(shù)
目標(biāo)
- 函數(shù)
- 定義格式
- 外部參數(shù)
- 無返回值的三種情況
- 閉包
- 閉包的定義
- 尾隨閉包
- 循環(huán)引用
- OC Block復(fù)習(xí)
函數(shù)
目標(biāo)
- 掌握函數(shù)的定義
- 掌握外部參數(shù)的用處
- 掌握無返回類型的三種函數(shù)定義方式
代碼實(shí)現(xiàn)
- 函數(shù)的定義
- 格式
func 函數(shù)名(行參列表) -> 返回值 {代碼實(shí)現(xiàn)}
- 調(diào)用
let result = 函數(shù)名(值1, 參數(shù)2: 值2...)
- 格式
//Swfit 3.0
func sum(a: Int, b: Int) -> Int {
return a + b
}
let result = sum(a:10, b: 20)
//Swfit 2.0
func sum(a: Int, b: Int) -> Int {
return a + b
}
let result = sum(10, b: 20)
- 沒有返回值的函數(shù)蜒车,一共有三種寫法
- 省略
- ()
- Void
func demo(str: String) -> Void {
print(str)
}
func demo1(str: String) -> () {
print(str)
}
func demo2(str: String) {
print(str)
}
demo("hello")
demo1("hello world")
demo2("olleh")
- 外部參數(shù)
- 在形參名前再增加一個(gè)外部參數(shù)名讳嘱,能夠方便調(diào)用人員更好地理解函數(shù)的語(yǔ)義
- 格式:
func 函數(shù)名(外部參數(shù)名 形式參數(shù)名: 形式參數(shù)類型) -> 返回值類型 { // 代碼實(shí)現(xiàn) }
- Swift 2.0 中,默認(rèn)第一個(gè)參數(shù)名省略
func sum1(num1 a: Int, num2 b: Int) -> Int {
return a + b
}
sum1(num1: 10, num2: 20)
- 函數(shù)的內(nèi)部函數(shù)
func demo() {
//該內(nèi)部函數(shù)的范圍只能在 demo函數(shù)中可以被訪問
func sum() {
print("哈哈哈")
}
//必須先聲明內(nèi)部函數(shù)才能調(diào)用調(diào)用
sum()
}
函數(shù)格式小結(jié)
// 格式:func 函數(shù)名(形參1: 類型 = 默認(rèn)值, _ 形參2: 類型 = 默認(rèn)值...) -> 返回值 { // 代碼實(shí)現(xiàn) }
// 說明:包含默認(rèn)值的函數(shù)可以不用傳遞醇王,并且可以任意組合
//
// 格式:func 函數(shù)名(形參1: 類型, _ 形參2: 類型...) -> 返回值 { // 代碼實(shí)現(xiàn) }
// 說明:_ 可以忽略外部參數(shù)呢燥,與其他語(yǔ)言的函數(shù)風(fēng)格更加類似
//
// 格式:func 函數(shù)名(外部參數(shù)1 形參1: 類型, 外部參數(shù)2 形參2: 類型...) -> 返回值 { // 代碼實(shí)現(xiàn) }
// 說明:外部參數(shù)名供外部調(diào)用使用,形參 在函數(shù)內(nèi)部使用
//
// 格式:func 函數(shù)名(形參列表) -> 返回值 { // 代碼實(shí)現(xiàn) }
閉包
與 OC 中的 Block 類似寓娩,閉包
主要用于異步操作執(zhí)行完成后的代碼回調(diào)叛氨,網(wǎng)絡(luò)訪問結(jié)果以參數(shù)的形式傳遞給調(diào)用方
目標(biāo)
- 掌握閉包的定義
- 掌握閉包的概念和用法
- 了解尾隨閉包的寫法
- 掌握解除循環(huán)引用的方法
OC 中 Block 概念回顧
- 閉包類似于 OC 中的
Block
- 預(yù)先定義好的代碼
- 在需要時(shí)執(zhí)行
- 可以當(dāng)作參數(shù)傳遞
- 可以有返回值
- 包含
self
時(shí)需要注意循環(huán)引用
閉包的定義
- 函數(shù)實(shí)際上是一個(gè)特殊的閉包, 函數(shù)知識(shí)閉包的一種新式
- 可以結(jié)合函數(shù)的使用 來學(xué)習(xí)閉包
- 定義一個(gè)函數(shù)在函數(shù)內(nèi)部定義一個(gè)沒有參數(shù)也沒有返回值的內(nèi)部函數(shù)
func demo1A() {
func sum() {
print("哈哈哈")
}
sum()
}
- 定義一個(gè)閉包 實(shí)現(xiàn)函數(shù)的內(nèi)部函數(shù)一樣的效果
- 閉包 = { (行參) -> 返回值 in // 代碼實(shí)現(xiàn) }
-
in
用于區(qū)分函數(shù)定義和代碼實(shí)現(xiàn)
//使用閉包實(shí)現(xiàn)
func demo1B() {
//定義代碼塊 () -> ()
//沒有參數(shù)沒有返回值的閉包
let closure = { () -> Void in
print("OK")
}
//執(zhí)行代碼塊
closure()
}
- 定義一個(gè)函數(shù),內(nèi)部函數(shù)有參數(shù)也有返回值
func demo3A() {
func sum(num1 a: Int, num2 b: Int) -> Int{
return a + b
}
let result = sum(num1: 10, num2: 20)
print(result)
}
- 定義一個(gè)函數(shù),實(shí)現(xiàn)上述函數(shù)的內(nèi)部函數(shù)
func demo3B() {
let closure = { (num1 a: Int, num2 b: Int ) -> Int in
return a + b
}
let result = closure(num1: 100, num2: 200)
print(result)
}
- 定義一個(gè)閉包 實(shí)現(xiàn)函數(shù)的內(nèi)部函數(shù)一樣的效果
- 閉包 = { (行參) -> 返回值 in // 代碼實(shí)現(xiàn) }
-
in
用于區(qū)分函數(shù)定義和代碼實(shí)現(xiàn)
- 最簡(jiǎn)單的閉包,如果沒有參數(shù)/返回值棘伴,則
參數(shù)/返回值/in
統(tǒng)統(tǒng)都可以省略- { 代碼實(shí)現(xiàn) }
let closure = {
print("hello")
}
尾隨閉包
- 當(dāng)閉包是函數(shù)的最后一個(gè)參數(shù)時(shí),函數(shù)的參數(shù)的小括號(hào)可以提前關(guān)閉,閉包可以寫在小括號(hào)后面當(dāng)做尾隨閉包
閉包的三種簡(jiǎn)寫形式只需要了解即可,在實(shí)際開發(fā)中做到看到不陌生即可,直接使用系統(tǒng)提示的類型
基本使用
GCD 異步
- 模擬在后臺(tái)線程加載數(shù)據(jù)
func loadData() {
DispatchQueue.global().async {
//假裝耗時(shí)
Thread.sleep(forTimeInterval: 2)
}
}
- 尾隨閉包寞埠,如果閉包是最后一個(gè)參數(shù),可以用以下寫法
自定義閉包參數(shù)焊夸,實(shí)現(xiàn)主線程回調(diào)
- 添加沒有參數(shù)仁连,沒有返回值的閉包
override func viewDidLoad() {
super.viewDidLoad()
loadData {
print("完成回調(diào)")
}
}
//異步請(qǐng)求網(wǎng)絡(luò)數(shù)據(jù)沒在主隊(duì)列中回調(diào) 更新UI
//TODO: 待會(huì)解釋該關(guān)鍵詞
//@escaping
func loadData(a: Int,finished: @escaping (String) -> () ) {
DispatchQueue.global().async {
//假裝耗時(shí)
Thread.sleep(forTimeInterval: 2)
//獲取結(jié)果
let res = "老王"
//在主隊(duì)列中回調(diào)數(shù)據(jù)
DispatchQueue.main.async {
//回調(diào)數(shù)據(jù) 只負(fù)責(zé)請(qǐng)求數(shù)據(jù) 不負(fù)責(zé)更新UI
//從外界傳遞閉包
finished(res)
}
}
}
- 添加回調(diào)參數(shù)
override func viewDidLoad() {
super.viewDidLoad()
//定義一個(gè)有參數(shù)沒有返回值的閉包當(dāng)做參數(shù)傳遞
let closure = { (res: String) -> () in
print("我是請(qǐng)求結(jié)果: \(res)")
}
loadData(a:20,finished: closure)
//使用尾隨閉包的方式滴啊用
loadData(a: 20) { (res) in
print(res)
}
}
循環(huán)引用
- 建立
NetworkTools
對(duì)象
class NetworkTools: NSObject {
/// 加載數(shù)據(jù)
///
/// - parameter finished: 完成回調(diào)
func loadData(finished: () -> ()) {
print("開始加載數(shù)據(jù)...")
// ...
finished()
}
deinit {
print("網(wǎng)絡(luò)工具 88")
}
}
- 實(shí)例化
NetworkTools
并且加載數(shù)據(jù)
class ViewController: UIViewController {
var tools: NetworkTools?
override func viewDidLoad() {
super.viewDidLoad()
tools = NetworkTools()
tools?.loadData() {
print("come here \(self.view)")
}
}
/// 與 OC 中的 dealloc 類似,注意此函數(shù)沒有()
deinit {
print("控制器 88")
}
}
運(yùn)行不會(huì)形成循環(huán)引用,因?yàn)?loadData 執(zhí)行完畢后饭冬,就會(huì)釋放對(duì) self 的引用
- 修改
NetworkTools
使鹅,定義回調(diào)閉包屬性
/// 完成回調(diào)屬性
var finishedCallBack: (()->())?
/// 加載數(shù)據(jù)
///
/// - parameter finished: 完成回調(diào)
func loadData(finished: () -> ()) {
self.finishedCallBack = finished
print("開始加載數(shù)據(jù)...")
// ...
working()
}
func working() {
finishedCallBack?()
}
deinit {
print("網(wǎng)絡(luò)工具 88")
}
運(yùn)行測(cè)試,會(huì)出現(xiàn)循環(huán)引用
解除循環(huán)引用
- 與 OC 類似的方法
/// 類似于 OC 的解除引用
func demo() {
weak var weakSelf = self
tools?.loadData() {
print("\(weakSelf?.view)")
}
}
- Swift 推薦的方法
loadData { [weak self] in
print("\(self?.view)")
}
- 還可以
loadData { [unowned self] in
print("\(self.view)")
}
閉包(Block) 的循環(huán)引用小結(jié)
-
Swift
-
[weak self]
-
self
是可選項(xiàng)昌抠,如果self已經(jīng)被釋放患朱,則為nil
-
-
[unowned self]
-
self
不是可選項(xiàng),如果self已經(jīng)被釋放炊苫,則出現(xiàn)野指針訪問
-
-
-
Objc
-
__weak typeof(self) weakSelf;
- 如果
self
已經(jīng)被釋放裁厅,則為nil
- 如果
-
__unsafe_unretained typeof(self) weakSelf;
- 如果
self
已經(jīng)被釋放,則出現(xiàn)野指針訪問
- 如果
-
面相對(duì)象
目標(biāo)
- 構(gòu)造函數(shù)
- 構(gòu)造函數(shù)的基本概念
- 構(gòu)造函數(shù)的執(zhí)行順序
-
KVC
在構(gòu)造函數(shù)中的使用及原理 - 便利構(gòu)造函數(shù)
- 析構(gòu)函數(shù)
- 區(qū)分
重載
和重寫
- 懶加載
- 只讀屬性(計(jì)算型屬性)
- 設(shè)置模型數(shù)據(jù)(
didSet
)
構(gòu)造函數(shù)基礎(chǔ)
構(gòu)造函數(shù)
是一種特殊的函數(shù)侨艾,主要用來在創(chuàng)建對(duì)象時(shí)初始化對(duì)象执虹,為對(duì)象成員變量
設(shè)置初始值,在 OC 中的構(gòu)造函數(shù)是 initWithXXX唠梨,在 Swift 中由于支持函數(shù)重載袋励,所有的構(gòu)造函數(shù)都是init
構(gòu)造函數(shù)的作用
- 分配空間
alloc
- 設(shè)置初始值
init
必選屬性
- 自定義
Person
對(duì)象
class Person: NSObject {
/// 姓名
var name: String
/// 年齡
var age: Int
}
提示錯(cuò)誤 Class 'Person' has no initializers
-> 'Person' 類沒有實(shí)例化器s
原因:如果一個(gè)類中定義了必選屬性,必須通過構(gòu)造函數(shù)為這些必選屬性分配空間并且設(shè)置初始值
-
重寫
父類的構(gòu)造函數(shù)
/// `重寫`父類的構(gòu)造函數(shù)
override init() {
}
提示錯(cuò)誤 Property 'self.name' not initialized at implicitly generated super.init call
-> 屬性 'self.name' 沒有在隱式生成的 super.init 調(diào)用前被初始化
- 手動(dòng)添加
super.init()
調(diào)用
/// `重寫`父類的構(gòu)造函數(shù)
override init() {
super.init()
}
提示錯(cuò)誤 Property 'self.name' not initialized at super.init call
-> 屬性 'self.name' 沒有在 super.init 調(diào)用前被初始化
- 為比選屬性設(shè)置初始值
/// `重寫`父類的構(gòu)造函數(shù)
override init() {
name = "張三"
age = 18
super.init()
}
小結(jié)
- 非 Optional 屬性姻成,都必須在構(gòu)造函數(shù)中設(shè)置初始值插龄,從而保證對(duì)象在被實(shí)例化的時(shí)候,屬性都被正確初始化
- 在調(diào)用父類構(gòu)造函數(shù)之前科展,必須保證本類的屬性都已經(jīng)完成初始化
- Swift 中的構(gòu)造函數(shù)不用寫
func
子類的構(gòu)造函數(shù)
- 自定義子類時(shí),需要在構(gòu)造函數(shù)中糠雨,首先為本類定義的屬性設(shè)置初始值
- 然后再調(diào)用父類的構(gòu)造函數(shù)才睹,初始化父類中定義的屬性
/// 學(xué)生類
class Student: Person {
/// 學(xué)號(hào)
var no: String
override init() {
no = "001"
super.init()
}
}
小結(jié)
- 先調(diào)用本類的構(gòu)造函數(shù)初始化本類的屬性
- 然后調(diào)用父類的構(gòu)造函數(shù)初始化父類的屬性
- Xcode 7 beta 5之后,父類的構(gòu)造函數(shù)會(huì)被自動(dòng)調(diào)用甘邀,強(qiáng)烈建議寫
super.init()
琅攘,保持代碼執(zhí)行線索的可讀性 -
super.init()
必須放在本類屬性初始化的后面,保證本類屬性全部初始化完成
Optional
屬性
- 將對(duì)象屬性類型設(shè)置為
Optional
class Person: NSObject {
/// 姓名
var name: String?
/// 年齡
var age: Int?
}
-
可選屬性
不需要設(shè)置初始值松邪,默認(rèn)初始值都是 nil -
可選屬性
是在設(shè)置數(shù)值的時(shí)候才分配空間的坞琴,是延遲分配空間的,更加符合移動(dòng)開發(fā)中延遲創(chuàng)建的原則
重載構(gòu)造函數(shù)
- Swift 中支持函數(shù)重載逗抑,同樣的函數(shù)名剧辐,不一樣的參數(shù)類型
/// `重載`構(gòu)造函數(shù)
///
/// - parameter name: 姓名
/// - parameter age: 年齡
///
/// - returns: Person 對(duì)象
init(name: String, age: Int) {
self.name = name
self.age = age
super.init()
}
注意事項(xiàng)
- 如果重載了構(gòu)造函數(shù),但是沒有實(shí)現(xiàn)默認(rèn)的構(gòu)造函數(shù)
init()
邮府,則系統(tǒng)不再提供默認(rèn)的構(gòu)造函數(shù) - 原因荧关,在實(shí)例化對(duì)象時(shí),必須通過構(gòu)造函數(shù)為對(duì)象屬性分配空間和設(shè)置初始值褂傀,對(duì)于存在必選參數(shù)的類而言忍啤,默認(rèn)的
init()
無法完成分配空間和設(shè)置初始值的工作
調(diào)整子類的構(gòu)造函數(shù)
-
重寫
父類的構(gòu)造函數(shù)
/// `重寫`父類構(gòu)造函數(shù)
///
/// - parameter name: 姓名
/// - parameter age: 年齡
///
/// - returns: Student 對(duì)象
override init(name: String, age: Int) {
no = "002"
super.init(name: name, age: age)
}
-
重載
構(gòu)造函數(shù)
/// `重載`構(gòu)造函數(shù)
///
/// - parameter name: 姓名
/// - parameter age: 年齡
/// - parameter no: 學(xué)號(hào)
///
/// - returns: Student 對(duì)象
init(name: String, age: Int, no: String) {
self.no = no
super.init(name: name, age: age)
}
注意:如果是重載的構(gòu)造函數(shù),必須
super
以完成父類屬性的初始化工作
重載
和重寫
-
重載
仙辟,函數(shù)名相同同波,參數(shù)名/參數(shù)類型/參數(shù)個(gè)數(shù)不同- 重載函數(shù)并不僅僅局限于
構(gòu)造函數(shù)
- 函數(shù)重載是面相對(duì)象程序設(shè)計(jì)語(yǔ)言的重要標(biāo)志
- 函數(shù)重載能夠簡(jiǎn)化程序員的記憶
- OC 不支持函數(shù)重載鳄梅,OC 的替代方式是
withXXX...
- 重載函數(shù)并不僅僅局限于
-
重寫
,子類需要在父類擁有方法的基礎(chǔ)上進(jìn)行擴(kuò)展未檩,需要override
關(guān)鍵字
KVC 字典轉(zhuǎn)模型構(gòu)造函數(shù)
/// `重寫`構(gòu)造函數(shù)
///
/// - parameter dict: 字典
///
/// - returns: Person 對(duì)象
init(dict: [String: Any]) {
setValuesForKeysWithDictionary(dict)
}
以上代碼編譯就會(huì)報(bào)錯(cuò)卫枝!
-
原因:
- KVC 是 OC 特有的,KVC 本質(zhì)上是在
運(yùn)行時(shí)
讹挎,動(dòng)態(tài)向?qū)ο蟀l(fā)送setValue:ForKey:
方法校赤,為對(duì)象的屬性設(shè)置數(shù)值 - 因此,在使用 KVC 方法之前筒溃,需要確保對(duì)象已經(jīng)被正確
實(shí)例化
- KVC 是 OC 特有的,KVC 本質(zhì)上是在
添加
super.init()
同樣會(huì)報(bào)錯(cuò)-
原因:
-
必選屬性
必須在調(diào)用父類構(gòu)造函數(shù)之前完成初始化分配工作
-
講必選參數(shù)修改為可選參數(shù)马篮,調(diào)整后的代碼如下:
/// 個(gè)人模型
class Person: NSObject {
/// 姓名
var name: String?
/// 年齡
var age: Int?
/// `重寫`構(gòu)造函數(shù)
///
/// - parameter dict: 字典
///
/// - returns: Person 對(duì)象
init(dict: [String: AnyObject]) {
super.init()
setValuesForKeysWithDictionary(dict)
}
}
運(yùn)行測(cè)試,仍然會(huì)報(bào)錯(cuò)
錯(cuò)誤信息:this class is not key value coding-compliant for the key age.
-> 這個(gè)類的鍵值 age 與 鍵值編碼不兼容
- 原因:
- 在 Swift 中怜奖,如果屬性是可選的浑测,在初始化時(shí),不會(huì)為該屬性分配空間
- 而 OC 中基本數(shù)據(jù)類型就是保存一個(gè)數(shù)值歪玲,不存在
可選
的概念
- 解決辦法:給基本數(shù)據(jù)類型設(shè)置初始值
- 修改后的代碼如下:
/// 姓名
var name: String?
/// 年齡
var age: Int? = 0
/// `重寫`構(gòu)造函數(shù)
///
/// - parameter dict: 字典
///
/// - returns: Person 對(duì)象
init(dict: [String: Any]) {
super.init()
setValuesForKeysWithDictionary(dict)
}
提示:在定義類時(shí)迁央,基本數(shù)據(jù)類型屬性一定要設(shè)置初始值,否則無法正常使用 KVC 設(shè)置數(shù)值
KVC 函數(shù)調(diào)用順序
init(dict: [String: Any]) {
super.init()
setValuesForKeysWithDictionary(dict)
}
override func setValue(value: Any?, forKey key: String) {
print("Key \(key) \(value)")
super.setValue(value, forKey: key)
}
// `NSObject` 默認(rèn)在發(fā)現(xiàn)沒有定義的鍵值時(shí)滥崩,會(huì)拋出 `NSUndefinedKeyException` 異常
override func setValue(value: Any?, forUndefinedKey key: String) {
print("UndefinedKey \(key) \(value)")
}
-
setValuesForKeysWithDictionary
會(huì)按照字典中的key
重復(fù)調(diào)用setValue:forKey
函數(shù) - 如果沒有實(shí)現(xiàn)
forUndefinedKey
函數(shù)岖圈,程序會(huì)直接崩潰- NSObject 默認(rèn)在發(fā)現(xiàn)沒有定義的鍵值時(shí),會(huì)拋出
NSUndefinedKeyException
異常
- NSObject 默認(rèn)在發(fā)現(xiàn)沒有定義的鍵值時(shí),會(huì)拋出
- 如果實(shí)現(xiàn)了
forUndefinedKey
钙皮,會(huì)保證setValuesForKeysWithDictionary
繼續(xù)遍歷后續(xù)的key
- 如果父類實(shí)現(xiàn)了
forUndefinedKey
蜂科,子類可以不必再實(shí)現(xiàn)此函數(shù)
子類的 KVC 函數(shù)
/// 學(xué)生類
class Student: Person {
/// 學(xué)號(hào)
var no: String?
}
- 如果父類中已經(jīng)實(shí)現(xiàn)了父類的相關(guān)方法,子類中不用再實(shí)現(xiàn)相關(guān)方法
convenience 便利構(gòu)造函數(shù)
- 條件判斷短条,只有滿足條件导匣,才實(shí)例化對(duì)象,可以防治造成不必要的內(nèi)存開銷
- 簡(jiǎn)化對(duì)象的創(chuàng)建
- 本身不負(fù)責(zé)屬性的創(chuàng)建和初始化工作
特點(diǎn)
- 默認(rèn)情況下茸时,所有的構(gòu)造方法都是指定構(gòu)造函數(shù)
Designated
-
convenience
關(guān)鍵字修飾的構(gòu)造方法就是便利構(gòu)造函數(shù) - 便利構(gòu)造函數(shù)具有以下特點(diǎn):
- 可以構(gòu)造失敗 返回
nil
, - 只有便利構(gòu)造函數(shù)中可以調(diào)用
self.init()
- 便利構(gòu)造函數(shù)不能被
重寫
或者super
- 可以構(gòu)造失敗 返回
/// `便利構(gòu)造函數(shù)`
///
/// - parameter name: 姓名
/// - parameter age: 年齡
///
/// - returns: Person 對(duì)象贡定,如果年齡過小或者過大,返回 nil
convenience init?(name: String, age: Int) {
if age < 20 || age > 100 {
return nil
}
self.init(dict: ["name": name, "age": age])
}
注意:在 Xcode 中可都,輸入
self.init
時(shí)沒有智能提示
/// 學(xué)生類
class Student: Person {
/// 學(xué)號(hào)
var no: String?
convenience init?(name: String, age: Int, no: String) {
self.init(name: name, age: age)
self.no = no
}
}
便利構(gòu)造函數(shù)應(yīng)用場(chǎng)景
- 根據(jù)給定參數(shù)判斷是否創(chuàng)建對(duì)象缓待,而不像指定構(gòu)造函數(shù)那樣必須要實(shí)例化一個(gè)對(duì)象出來
- 在實(shí)際開發(fā)中,可以對(duì)已有類的構(gòu)造函數(shù)進(jìn)行擴(kuò)展汹粤,利用便利構(gòu)造函數(shù)命斧,簡(jiǎn)化對(duì)象的創(chuàng)建
構(gòu)造函數(shù)小結(jié)
- 指定構(gòu)造函數(shù)必須調(diào)用其直接父類的的指定構(gòu)造函數(shù)(除非沒有父類)
- 便利構(gòu)造函數(shù)必須調(diào)用同一類中定義的其他
指定構(gòu)造函數(shù)
或者用self.
的方式調(diào)用父類的便利構(gòu)造函數(shù)
- 便利構(gòu)造函數(shù)可以返回
nil
- 便利構(gòu)造函數(shù)可以被繼承
懶加載
在 iOS 開發(fā)中,懶加載是無處不在的
- 懶加載的格式如下:
lazy var person: Person = {
print("懶加載")
return Person()
}()
- 懶加載本質(zhì)上是一個(gè)閉包
- 懶加載的簡(jiǎn)單寫法
lazy var demoPerson: Person = Person()