小視頻
001--swift簡史小視頻
002--Playground體驗(yàn)
003--常量&變量
一迹卢、swift簡史
1、介紹
swift是蘋果公司于2014年推出用于撰寫OS和iOS應(yīng)用程序的語言臣嚣。它由蘋果開發(fā)者工具部門總監(jiān)“克里斯.拉特納”在2010年開始著手設(shè)計(jì)颅停,歷時(shí)一年完成基本的架構(gòu)惫搏。到后來蘋果公司大力投入swift語言的研發(fā)具温,于2014年發(fā)布這一語言的第一版本。swift2.0之后的語法則趨于穩(wěn)定筐赔,2017年發(fā)布的swift4.0雖有改動(dòng)铣猩,但也只是增添了一些新特性。這些新特性需要在Xcode9上運(yùn)行才能顯示出效果茴丰。值得一提的是它支持unicode9,也就是說达皿,可以用某些圖片圖標(biāo)來充當(dāng)變量。
例如:
"????".count // 人 + 膚色
"???????????".count // 有4個(gè)成員的家庭
"????\u{200D}????\u{200D}????\u{200D}????".count // 家庭 + 膚色
"???????".count // 人 + 膚色 + 職業(yè)
2较沪、特點(diǎn)
- swift取消了預(yù)編譯指令鳞绕,宏也被包括在內(nèi)。
某些開發(fā)者為了讓Objective-C和swift代碼兼容尸曼,會(huì)盡少在Objective-C中定義宏。 - 取消了Objective-C中的指針等其他不安全訪問的使用
- 使用點(diǎn)語法來調(diào)用屬性或者函數(shù)
- 去除了NS前綴
3萄焦、為什么要學(xué)習(xí)swift
- swift作為面向協(xié)議語言控轿,不僅能寫移動(dòng)端冤竹,也可以做到搭建服務(wù)器端。
- 縱觀國內(nèi)外iOS開發(fā)界茬射,已經(jīng)有許多公司直接或間接采用swift開發(fā)鹦蠕,使用swift語言開發(fā)已成為未來iOS開發(fā)的趨勢。
- swift以簡潔在抛、優(yōu)雅等優(yōu)點(diǎn)迅速俘獲廣大開發(fā)者的青睞钟病。
二、用playground體驗(yàn)swift開發(fā)
- 打開Xcode,選擇創(chuàng)建一個(gè)playground項(xiàng)目
- 創(chuàng)建一個(gè)普通的UIView對象
正如上圖所示刚梭,playgound文件的左邊是代碼區(qū)肠阱,右邊則是顯示結(jié)果的區(qū)域。當(dāng)點(diǎn)擊用于眼睛時(shí)會(huì)實(shí)時(shí)顯示出界面效果朴读。
-
swift與objective-C的重大區(qū)別
- 在swift中是沒有.h和.m文件之分的屹徘。所有的代碼全部都存儲(chǔ)在一個(gè)文件里面。
- 在swift中所有的代碼都被封裝在{}里面
- OC使用alloc init進(jìn)行初始化衅金,而swift使用()
- OC中使用[]來調(diào)用方法噪伊,而swift中采用點(diǎn)語法。比如
UIColor.red
- swift中不需要用分號分割語句
三氮唯、常量和變量
1鉴吹、數(shù)據(jù)類型
在swift中也有各種數(shù)據(jù)類型來存儲(chǔ)不同的信息。下表列舉的是常見的數(shù)據(jù)類型變量惩琉。
但其實(shí)拙寡,在swift中,是不存在基本的數(shù)據(jù)類型的琳水,所謂的數(shù)據(jù)類型肆糕,其實(shí)都只是結(jié)構(gòu)體。這也是swift中的一個(gè)特點(diǎn)在孝。
2诚啃、變量和常量
- 聲明
swift中用let
聲明常量,用var
聲明變量私沮。
var x = 10;
let y = 20;
let z //錯(cuò)誤示范始赎,let z 在聲明的時(shí)候并沒有賦值常量是不可改變的,只能在聲明時(shí)賦值
在開發(fā)中仔燕,通常會(huì)優(yōu)先選擇使用let造垛,因?yàn)椴豢勺儠?huì)更安全一點(diǎn)。所以建議在寫代碼之時(shí)晰搀,先選擇let五辽,等到需要變化的時(shí)候再改成var。
- 自動(dòng)推導(dǎo)
創(chuàng)建一個(gè)UIView,不指定類型外恕「硕海可以看到控制臺(tái)上會(huì)打印出UIView的信息乡翅。這個(gè)現(xiàn)象被稱為swift的自動(dòng)推導(dǎo)。事實(shí)上罪郊,在代碼左側(cè)定義的類型只是程序員希望的類型蠕蚜,而右側(cè)才是程序真實(shí)的類型。
let z = UIView()
print(z)
也就是說悔橄,變量或常量的類型會(huì)根據(jù)右側(cè)代碼執(zhí)行的結(jié)果靶累,推導(dǎo)出對應(yīng)的類型。
可以使用熱鍵option
點(diǎn)擊查看類型癣疟。
- swift對類型的嚴(yán)格要求
在swift中挣柬,任何不同類型的數(shù)據(jù)之間是不允許直接運(yùn)算的。比如下面這段代碼就會(huì)報(bào)錯(cuò)争舞。
//錯(cuò)誤示范
let a = 10
let b = 12.5
print(x + y)
如果非要讓不同類型數(shù)據(jù)之間能夠運(yùn)算凛忿,可以將其中一個(gè)類型進(jìn)行轉(zhuǎn)換。
let a = 10
let b = 12.5
print(a + Int(b))
此時(shí)得到的結(jié)果就是22竞川。在swift中店溢,做類型轉(zhuǎn)換時(shí)是將數(shù)據(jù)括起來,相當(dāng)于swift結(jié)構(gòu)體中的構(gòu)造函數(shù)委乌。
當(dāng)然也可以將前面的整數(shù)轉(zhuǎn)換成Double型床牧。此時(shí)就能打印出小數(shù)來。
print(Double(a)+b)
四遭贸、String類型和Bool類型
1戈咳、String類型
- 聲明
直接用雙引號將數(shù)據(jù)引起來
let str = "小仙女"
let str1:String = "hahh"
- 拼接
字符串的連接有兩種方法,一種是通過加號來連接壕吹,另一種則是通過反斜桿進(jìn)行插入著蛙。
let str = "小仙女"
let mesg1 = "one"+str //用加號的方式
let mesg2 = "two,\(str)" //反斜杠的方式
print(mesg1,mesg2)
在做字符串拼接時(shí)要注意加號和反斜杠后面都不能出現(xiàn)空格,不然會(huì)報(bào)錯(cuò)耳贬。
- 拼接字符串時(shí)格式的變化
假設(shè)在某些特定的地方需要輸出特定位數(shù)的字符踏堡,比如或時(shí)間的輸出,就需要使用占位符來調(diào)整字符串的格式咒劲。使用String的構(gòu)造函數(shù)顷蟆,調(diào)用format方法,%0后面加上數(shù)字就表示需要占多少位數(shù)腐魂。
let min = 2
let second = 10
String(format: "%02d:%02d", min,second)
- 遍歷
調(diào)用字符串的characters屬性帐偎,采用for...in...的方式來遍歷字符串。
for c in str{
print(c) //swift4中的遍歷
}
print(str.count) //打印字符串長度
for char in myString.characters {
print(char) // swift3的遍歷
}
print(str..characters.count) //swift3打印字符串長度
- 字符串的截取
最方便的方式就是將String類型轉(zhuǎn)換成OC的NSString類型蛔屹,再來截取削樊。
let urlStr = "www.baidu.com"
let header = (urlStr as NSString).substring(to: 3) //截取前三位
let middle = (urlStr as NSString).substring(with: NSMakeRange(4, 5))//去除前四個(gè)字符截取,范圍之后五位字符
let footer = (urlStr as NSString).substring(from: 10) //從第十個(gè)字符開始截取
2判导、Bool類型
與其他語言一樣嫉父,Bool類型表示的就是真假沛硅,但是不同于Objective-C眼刃,swift中用true和false來表示真假绕辖。
五、可選類型
在Objective-C開發(fā)中擂红,如果一個(gè)變量暫時(shí)不會(huì)使用到仪际,可以將它賦值為0或者賦值為空,而在swift中昵骤,nil是一個(gè)特殊的類型树碱,如果它和真實(shí)類型不匹配是不能進(jìn)行賦值的。但是開發(fā)中將變量賦值為空是在所難免的事情变秦,因此就推出了可選類型成榜。
可選類型是swift的一大特色,在定義變量時(shí)蹦玫,如果指定這個(gè)變量是可選的話赎婚,就是說這個(gè)變量可以有一個(gè)指定類型的值或者為nil。
1樱溉、定義一個(gè)optional的變量
let x:Optional = 10
print(x)
點(diǎn)擊進(jìn)去查看挣输,可以發(fā)現(xiàn)Option其實(shí)是一個(gè)枚舉類型。這個(gè)枚舉有兩個(gè)值福贞,一個(gè)是none撩嚼,表示沒有值,而另一個(gè)是some挖帘,表示某一類值完丽。
在輸出的時(shí)候,可以看見控制臺(tái)上的內(nèi)容Optional(10)
拇舀,它的作用就是提示這是一個(gè)可選值逻族。
而在實(shí)際開發(fā)中,一般不用上述方式創(chuàng)建可選值你稚,而是指定一個(gè)類型瓷耙,再在其后添一個(gè)問號。
let x:Optional = 10 //第一種寫法
let x:Int? = 20 //第二種寫法
print(x)
上述代碼問號的意思就是定義一個(gè)可選的Int類型刁赖,可能沒有值搁痛,也可能有一個(gè)整數(shù)。
2宇弛、 解包
試試將上面案例x和y相加鸡典,這個(gè)時(shí)候還能輸出結(jié)果么?
此時(shí)可以看到編譯器已經(jīng)報(bào)錯(cuò)枪芒。在前面的教程中提到過彻况,不同類型的值是不能直接運(yùn)算的谁尸。而可選項(xiàng)有兩種值的產(chǎn)生,若它的值為nil則不能參加計(jì)算纽甘。
因此引入解包的概念良蛮,“!”代表強(qiáng)制解包。它的意思是從可選值中強(qiáng)行獲取對應(yīng)的非空值悍赢。
print(x!+y!)
3决瞳、解包常見錯(cuò)誤
//錯(cuò)誤示范1
let y : Int?
print(y)
使用let定義的是常量,在初始化時(shí)必須要給出值左权。
//錯(cuò)誤示范2:
let y : Int? = nil
print(y)
強(qiáng)制解包是危險(xiǎn)操作皮胡,如果可選值為nil,強(qiáng)制解包系統(tǒng)會(huì)奔潰赏迟。
4屡贺、let和var的可選項(xiàng)默認(rèn)值
//默認(rèn)值測試
let x: Int?
print(x)
var y :Int?
print(y)
用let做測試時(shí)會(huì)直接報(bào)錯(cuò),說明let的可選值是沒有默認(rèn)值的锌杀,而用var做測試時(shí)甩栈,報(bào)錯(cuò)信息就變成了警告,運(yùn)行的結(jié)果為nil抛丽“埃可以由此推測出var的可選項(xiàng)默認(rèn)值為nil。
swift中有規(guī)定亿鲜,對象中的任何屬性在創(chuàng)建對象時(shí)允蜈,都必須有明確的初始化值。
5蒿柳、可選綁定
用if let/var
表示饶套。它將變量賦值給一個(gè)臨時(shí)變量,在這個(gè)操作中會(huì)做兩步操作:首先判斷變量是否有值垒探,如果沒有值妓蛮,則直接不執(zhí)行大括號里面的內(nèi)容;如果有值圾叼,系統(tǒng)會(huì)自動(dòng)將變量進(jìn)行解包蛤克,并且將解包后的結(jié)果,賦值給臨時(shí)變量夷蚊。
比如下面這個(gè)例子:
通過一個(gè)字符串創(chuàng)建NSURL對象
let url: URL? = URL(string: "https://www.baidu.com")
接著創(chuàng)建NSURLRequest對象构挤。強(qiáng)制解包非常危險(xiǎn),當(dāng)url有中文的時(shí)候可能會(huì)變成nil惕鼓。所以要判斷url是否為空再對其進(jìn)行解包筋现。
if let url = url {
let request = URLRequest(url: url)
}
六、swift中的分支
1、if語句
在swift中矾飞,if語句是不用帶小括號的一膨,但是后面跟的語句必須有花括號,哪怕只有一行代碼洒沦。許多公司的代碼規(guī)范也是規(guī)定必須使用這一格式豹绪。
注意:在swift中沒有非0即真的說法,所以不能寫成if(num)
這樣的格式微谓。
let x = 9
if x > 5 {
print("小仙女")
}else{
print("妖精哪里跑")
}
2森篷、三目運(yùn)算符
三目運(yùn)算符的寫法是表達(dá)式后跟一個(gè)問號输钩,用冒號來隔開條件是否成立的值豺型。
let x = 10
x > 5 ? print("小仙女"):print("妖精")
非常有意思的是,如果開發(fā)者只想處理?xiàng)l件成立的部分买乃,此時(shí)可以在冒號后面用一個(gè)小括號來代替條件不成立的部分姻氨。
x > 5 ? print("你都寫了我兩次啦"):()
3、 三目運(yùn)算符的簡單模式
三目運(yùn)算符的簡單模式通常是用于處理可選項(xiàng)的剪验‰群福“?功戚?”的意思是說娶眷,如果表達(dá)式有值,就使用那個(gè)值啸臀,如果沒有届宠,就使用“??”后面的值來代替。
let x:Int? = nil
let y:Int? = 9
print((x ?? 0) + (y ?? 0))
運(yùn)行之后的結(jié)果為9乘粒。
之后再來說說運(yùn)算符的優(yōu)先級豌注。舉個(gè)簡單的栗子??!
let name:String? = "安琪拉"
print((name ?? "") + "火燒屁屁咯")
print(name ?? "" + "火燒屁屁咯")
從運(yùn)行的結(jié)果可以看到灯萍,“轧铁??”的優(yōu)先級是最低的旦棉。如果沒有小括號的約束齿风,它會(huì)將后面的語句都當(dāng)成是一個(gè)表達(dá)式。
4绑洛、 guard的用法
分支若是寫得過多救斑,就會(huì)導(dǎo)致代碼可讀性較差的問題。為了降低代碼的層次诊笤,swift推出了guard系谐。guard后面跟判斷表達(dá)式,else后面寫表達(dá)式不成立的代碼。
需要注意的是guard必須寫在函數(shù)內(nèi)部纪他,在最末尾出必須要跟關(guān)鍵字return/continue/break/throw
中的一種鄙煤。
import UIKit
let age = 20
func online(age : Int){
guard age >= 18 else {
print("還未成年呢")
return
}
print("一起來開黑吖")
}
這樣或許看不到guard的特別之處,但若是像下面這樣的代碼出現(xiàn)呢茶袒?
let age = 20
let money = true
let idcard = true
func online2(age : Int,money:Bool,idcard:Bool){
if age >= 18 {
if money {
if idcard {
print("一起來開黑吖")
}else{
print("回去帶身份證吧")
}
}else{
print("回去拿錢")
}
}else {
print("還未成年呢")
}
}
//調(diào)用
online2(age: age, money: money, idcard: idcard)
如果用普通的分支方法梯刚,就會(huì)顯得可讀性太差。我們可以試著將它改成guard的寫法薪寓。
func online1(age : Int){
//判斷年齡
guard age >= 18 else {
print("還未成年呢")
return
}
//判斷是否有錢
guard money else {
print("回去拿錢")
return
}
//判斷是否帶了身份證
guard idcard else {
print("回去帶身份證吧")
return
}
print("一起來開黑吖")
}
執(zhí)行完所有的判斷語句之后才執(zhí)行代碼庫,閱讀性也比if……else分支強(qiáng)亡资。
5、 switch
- 最基本的用法
switch后面的小括號可以省略向叉。用case關(guān)鍵字來表示不同的情形锥腻,case語句結(jié)束后,break也可以省略母谎。
let sex = 0
switch sex {
case 0:
print("男")
case 1:
print("女")
default:
print("其他")
}
- 基礎(chǔ)語法的補(bǔ)充
如果系統(tǒng)某一個(gè)case中產(chǎn)生case穿透瘦黑,可以在case結(jié)束后跟上fallthrough
case 0:
print("男")
fallthrough
case后面可以判斷多個(gè)條件,這些條件以逗號分開
let sex = 0
switch sex {
case 0,1:
print("正常人")
default:
print("其他")
}
switch可以判斷浮點(diǎn)型奇唤、字符串類型和Bool類型
switch 3.14 {
case 0:
print("正常人")
default:
print("其他")
}
let opration = "+"
switch opration {
case "+":
print("加法")
case "-":
print("減法")
default:
print("其他")
}
七幸斥、swift的for循環(huán)和表示區(qū)間
1、變化
在swift3開始咬扇,就已經(jīng)棄用了var i = 0; i < 10; i++
的這種寫法甲葬。并且++
這種寫法也被取消掉了,改為+=代替懈贺。
2经窖、表示區(qū)間
swift常見區(qū)間有兩種,開區(qū)間用..<
表示隅居,閉區(qū)間用...
表示钠至。要注意的是數(shù)字和省略號之間是不能加空格的。
func demo1() {
for i in 0..<5 {
print(i)
}
print("^^^^^^^")
for i in 0...5 {
print(i)
}
}
demo1()
3胎源、逆序操作
如果想要做逆序操作棉钧,只要在in后面的表達(dá)式后添加reversed()
即可。
func demo1() {
for i in (0..<5).reversed() {
print(i)
}
}
demo1()
八涕蚤、swift中的數(shù)組
Swift語言提供了Arrays宪卿、Sets和Dictionaries三種基本的集合類型用來存儲(chǔ)集合數(shù)據(jù)。數(shù)組是有序數(shù)據(jù)的集万栅,集合是無序無重復(fù)數(shù)據(jù)的集佑钾,而字典則是無序的鍵值對的集。
數(shù)組使用有序列表存儲(chǔ)同一類型的多個(gè)值烦粒。相同的值可以多次出現(xiàn)在一個(gè)數(shù)組的不同位置中休溶。
1代赁、定義數(shù)組
用let定義出來的數(shù)組就是不可變的
//定義不可變數(shù)組
let array = ["愛麗絲","小紅帽","白雪公主"]
使用var來定義可變數(shù)組。正確的寫法是Array<Element>
這樣的形式兽掰。其中Element是這個(gè)數(shù)組中唯一允許存在的數(shù)據(jù)類型芭碍。但是為了簡便,推薦使用[Element]()
的寫法孽尽。
//定義可變數(shù)組
var arrayM = [String]()
var arrayM1:[String]
var arrayM2 = Array<String>()
2窖壕、創(chuàng)建帶有默認(rèn)值的數(shù)組
swift中的array類型還提供一個(gè)可以創(chuàng)建特定大小并且所有數(shù)據(jù)都被默認(rèn)的構(gòu)造方法。開發(fā)者可以在里面指定它的數(shù)量和類型杉女。
var threeDouble = Array(repeating: 0.0, count: 3)
print(threeDouble[1])
3瞻讽、對可變數(shù)組的基本操作
使用append給數(shù)組添加元素
arrayM.append("1")
arrayM.append("2")
arrayM.append("3")
arrayM.append("4")
arrayM.append("5")
使用insert方法將值添加到具體索引值之前
arrayM.insert("10", at: 2)
使用remove系列方法可以對數(shù)組做刪除操作
arrayM.remove(at: 0)
arrayM.removeSubrange(1..<3)
arrayM.removeAll()
arrayM.removeLast() //可以去除最后一項(xiàng),避免捕獲數(shù)組count屬性
通過取下標(biāo)的方式對數(shù)組進(jìn)行修改和查找
arrayM[0] = "小紅帽"
print(arrayM[2])
利用區(qū)間對具體范圍內(nèi)的值替換
//替換第2項(xiàng)和第3項(xiàng)的值
arrayM[2...4] = ["22","33"]
print(arrayM[3])
4、數(shù)組的遍歷
//根據(jù)下標(biāo)值進(jìn)行遍歷
for i in 0..<arrayM.count {
print(arrayM[i])
}
//直接遍歷數(shù)組中的元素
for i in arrayM {
print(i)
}
若同時(shí)需要每個(gè)數(shù)據(jù)項(xiàng)的值和索引,可以使用數(shù)組的emumerated()
方法來進(jìn)行數(shù)組遍歷锚扎。
for(index,value) in arrayM.enumerated(){
print(String(index+1)+":"+value)
}
5、數(shù)組的合并
只有相同類型的數(shù)組才能進(jìn)行合并快集。
let resultArray = arrayM + array
九、swift中的集合
集合(Set)用來存儲(chǔ)相同類型并且沒有確定順序的值廉白。當(dāng)集合元素順序不重要時(shí)或者希望確保每個(gè)元素只出現(xiàn)一次時(shí)可以使用集合而不是數(shù)組。
集合中的元素必須有確定的hashvalue乖寒,或者是實(shí)現(xiàn)了hashable協(xié)議猴蹂。而swift提供的Int,String等類型其實(shí)都是實(shí)現(xiàn)了hashable協(xié)議的。hashable是equable的子協(xié)議,如果要判斷兩個(gè)元素是否相等楣嘁,就要看他們的hashvalue是否相等磅轻。
1、定義集合
使用set<Element>
定義逐虚。
Element
表示集合中允許存儲(chǔ)的類型聋溜,和數(shù)組不同的是,集合沒有等價(jià)的簡化形式叭爱。
//創(chuàng)建空集合
var letters = Set<Character>()
//使用字面量創(chuàng)建集合
var favorite:Set<String> = ["綺羅生","意琦行"]
要注意的是一個(gè)Set類型是不能直接后面跟的字面量被單獨(dú)推斷出來的撮躁,因此這個(gè)Set
是必須要顯示聲明的。但是由于swift的自動(dòng)推斷功能买雾,可以不用寫出Set的具體類型把曼。比如說上面那個(gè)例子,省去String漓穿,也能推斷出Set的正確類型嗤军。
var favorite:Set = ["綺羅生","意琦行"]
2、訪問和修改集合
通過.count
屬性知道集合的長度晃危,通過isEmpty
判斷集合是否為空叙赚。
3、添加元素
favorite.insert("寒煙翠")
print(favorite.count)
4、刪除元素
通過remove
的方法刪除元素震叮,若這個(gè)值真的存在就會(huì)刪除改值沿量,并且返回被刪除的元素。若集合中不包含這個(gè)值冤荆,就會(huì)返回nil朴则。
if let removeBack = favorite.remove("意琦行"){
print(removeBack)
}else{
print("沒有找到值")
}
5、集合操作
swift提供了許多數(shù)學(xué)方法來操作集合钓简。
print(oddD.union(evenD).sorted()) //并集
print(oddD.intersection(evenD).sorted())//交集
print(oddD.subtracting(siggleDPrime).sorted())//取差值
print(oddD.symmetricDifference(siggleDPrime).sorted())//去掉相同值
6乌妒、遍歷集合
for item in favorite {
print(item)
}
//按照首字母的順序輸出
for item1 in favorite.sorted() {
print(item1)
}
7、集合的成員關(guān)系
用 ==
來判斷兩個(gè)集合是否包含全部相同的值
用 isSubset(of:)
來判斷一個(gè)集合中的值是否也被包含在另外一個(gè)集合中
用 isSuperset(of:)
來判斷一個(gè)集合中包含另一個(gè)集合所有的值
用isStrictSubset(of:)
或者isStrictSuperset(of:)
方法來判斷一個(gè)集合是否是另外一個(gè)集合的子集合或父集合并且兩個(gè)集合不相等
十外邓、字典
字典是一種存儲(chǔ)多個(gè)相同類型的值的容器撤蚊。每個(gè)值value
都關(guān)聯(lián)這唯一的鍵key
。鍵就是這個(gè)字典的標(biāo)識符损话。而且字典中的數(shù)據(jù)項(xiàng)并沒有具體順序侦啸。鍵集合不能有重復(fù)元素,而值集合是可以重復(fù)的丧枪。
1光涂、定義字典
使用let
定義不可變的字典,使用var
定義可變字典拧烦。用字面量賦值時(shí)忘闻,系統(tǒng)會(huì)自動(dòng)判斷[]
中存放的是鍵值對還是要一個(gè)個(gè)的元素。
let dict = [1:"one",2:"two",3:"three"] //定義不可變字典
var dictM = Dictionary<String,NSObject>() //定義可變字典
var dictM1 = [String:NSObject]()
//AnyObject一般用于指定類型恋博,NSObject一般用于創(chuàng)建對象
2齐佳、對可變字典做基本操作
添加、刪除和獲取元素
dictM1["name"] = "小仙女" as NSObject
dictM["age"] = 17 as NSObject
dictM.removeValue(forKey:"name")
//獲日凇:swift中只保留了最簡單的寫法炼吴,OC中有objectforkey的方法在swift中也被刪除掉了。
dictM["name"]
3疫衩、修改元素
若字典中已經(jīng)有對應(yīng)的key硅蹦,操作的結(jié)果是直接修改原來的key中保存的value。若字典中沒有對應(yīng)的key隧土,則會(huì)添加新的鍵值對提针。
dictM["name"] = "llx"
4、遍歷字典
可以通過范圍for遍歷所有的key和value曹傀。也可以遍歷所有的鍵值對辐脖。
for (key,value) in dictM {
print(key)
print(value)
}
5、合并字典
合并字典時(shí)通過遍歷的方式將第二個(gè)字典的內(nèi)容添加到第一個(gè)字典中皆愉。絕對不能用相加的方式對字典進(jìn)行合并嗜价。
var dict1 = ["name":"llx","age":"17"]
var dict2 = ["num":"007"]
for (key,value) in dict2 {
dict1[key] = value
}
dict
十一艇抠、元組
元組是swift中特有的一種數(shù)據(jù)結(jié)構(gòu),用于定義一組數(shù)據(jù)久锥,元組在數(shù)學(xué)中的應(yīng)用十分廣泛家淤。
1、定義元組
使用()
包含信息瑟由,組成元組類型的數(shù)據(jù)可以被稱為“元素”絮重。
//使用元組來描述個(gè)人信息
let info1 = ("1001","張三",30)
2、起別名
可以給元素加上名稱歹苦,之后可以通過元素名稱訪問元素
//給元素加上名稱青伤,之后可以通過元素名稱訪問元素
let info2 = (id:"1001",name:"張三",age:30)
info2.name
元組一般用于作為方法的返回值。元組中元素的別名殴瘦,就是元組的名稱
let (name,age) = ("張三",18)
name
十二狠角、函數(shù)
函數(shù)相當(dāng)于Objective-C中的方法,是一段完成特定任務(wù)的獨(dú)立代碼片段蚪腋》岣瑁可以通過給函數(shù)命名來標(biāo)志某個(gè)函數(shù)的功能。而這個(gè)名字可以用來在需要的時(shí)候“調(diào)用”該函數(shù)完成其任務(wù)屉凯。格式如下:
func 函數(shù)名(參數(shù)列表)-> 返回值類型 {
代碼塊
return 返回值
}
func表示關(guān)鍵字立帖,多個(gè)參數(shù)列表之間用逗號隔開,也可以沒有參數(shù)神得。使用->
指向返回值類型厘惦。如果沒有返回值,可以用Void
代替哩簿,也可以省略。
1酝静、定義無參無返回的函數(shù)
func phone()->Void {
print("小米")
}
phone()
2节榜、定義有參無返回的函數(shù)
func phoneNum() -> String {
return "123456"
}
print(phoneNum())
3、定義有參無返回的函數(shù)
func callPhone(phoneNum:String){
print("打電話給\(phoneNum)")
}
callPhone(phoneNum: "123456")
4别智、定義有參有返回的函數(shù)
func sum(num1 : Int,num2 : Int) -> Int{
return num1 + num2
}
sum(num1: 30, num2: 30)
在swift4之后宗苍,調(diào)用函數(shù)的時(shí)候,能直觀的看到參數(shù)薄榛。而在之前調(diào)用之時(shí)讳窟,只能看見第二個(gè)參數(shù)之后的名稱,表達(dá)起來并不直觀敞恋。如何解決這個(gè)問題呢丽啡?
可以采用給參數(shù)起別名的方式,在參數(shù)前面添加一個(gè)別名硬猫。
func sum(number1 num1: Int,number2 num2 : Int) -> Int{
return num1 + num2
}
sum(number1: 2, number2: 4)
5补箍、默認(rèn)參數(shù)
在swift中可以給方法的參數(shù)設(shè)置默認(rèn)值改执。比如說買甜筒的時(shí)候,商店默認(rèn)會(huì)給顧客準(zhǔn)備原味冰淇淋坑雅。但是用戶也可以選擇指定口味辈挂。
func makeIceCream(flavor:String = "原味") -> String {
return "制作一個(gè)\(flavor)冰淇淋"
}
makeIceCream()
makeIceCream(flavor: "抹茶")
6、可變參數(shù)
有些時(shí)候裹粤,在創(chuàng)建方法的時(shí)候终蒂,并不確定參數(shù)的個(gè)數(shù),于是swift推出了可變參數(shù)遥诉。參數(shù)的類型之后使用...
表示多個(gè)參數(shù)拇泣。
func sum(num:Int...) -> Int {
var result = 0
for i in num {
result += i
}
return result
}
sum(num: 18,29,3)
7、引用傳遞
如果現(xiàn)在有這樣一個(gè)需求:要交換兩個(gè)數(shù)的值突那,不能使用系統(tǒng)提供的方法挫酿。要如何來完成呢?
如果按照上面的寫法就會(huì)報(bào)錯(cuò)愕难,可以按住option
鍵查看早龟,參數(shù)默認(rèn)是不可變的。
而且就算可行猫缭,做到的也是值傳遞葱弟。為了解決這一問題,swift提供了關(guān)鍵字inout
來聲明數(shù)據(jù)地址傳遞猜丹,也被稱之為引用傳值芝加。在swift3.0的時(shí)候,inout的位置發(fā)生了改變射窒,被放置在標(biāo)簽位置藏杖。但是作用與之前相同。
func swapNum1( m : inout Int, n : inout Int) {
let tempNum = m
m = n
n = tempNum
}
swapNum1(m: &m, n: &n)
print("m:\(m),n:\(n)")
十三脉顿、類
swift用關(guān)鍵字class
來定義類蝌麸。通常情況下,定義類時(shí)艾疟,讓它繼承自NSObject
来吩,若沒有指定父類,那么該類就是rootClass
蔽莱。類的格式如下:
class 類名:SuperClass {
//定義屬性和方法
}
1弟疆、定義存儲(chǔ)屬性和創(chuàng)建類對象
對象的屬性必須要賦值,用解包的方式賦值為nil盗冷。
class Person : NSObject {
//定義存儲(chǔ)屬性
var age : Int = 0
var name : String? //對象的屬性必須賦值,不賦值會(huì)報(bào)錯(cuò)的哦
}
let p = Person()
2怠苔、給類的屬性賦值
可以直接賦值,也可以通過KVC進(jìn)行賦值
p.age = 10
p.name = "llx"
if let name = p.name {
print(name)
}
3正塌、定義方法
在swift中嘀略,如果使用當(dāng)前某一對象的屬性或者方法恤溶,可以直接使用,不需要加self
// 定義方法帜羊,返回平均成績
func getAverage() -> Double {
return (mathScore + EnglishScore)*0.5
}
let average = p.getAverage()
4咒程、定義計(jì)算屬性
通過別的方式計(jì)算到結(jié)果的屬性,稱之為計(jì)算屬性讼育。
var averageS : Double {
return (mathScore + EnglishScore) * 0.5
}
5帐姻、定義類屬性
類屬性是和整個(gè)類相關(guān)的屬性,用static修飾奶段,作用域是整個(gè)類饥瓷。通過類名進(jìn)行訪問。
static var courseCount : Int = 0
在類外通過類名訪問類屬性
Person.courseCount = 2
6痹籍、類的構(gòu)造函數(shù)
構(gòu)造函數(shù)類似于OC中的init方法呢铆。默認(rèn)情況下創(chuàng)建一個(gè)類時(shí),必定會(huì)調(diào)用一個(gè)構(gòu)造函數(shù)蹲缠。如果一個(gè)類繼承自NSObjct棺克,可以對父類的構(gòu)造函數(shù)進(jìn)行重寫。
在構(gòu)造函數(shù)中线定,如果沒有明確super.init()
娜谊。那么系統(tǒng)會(huì)默認(rèn)調(diào)用super.init()
。
class Person : NSObject {
var name : String?
var age : Int = 0
override init() {
print("hello world")
}
}
let p = Person()
7斤讥、自定義構(gòu)造函數(shù)
自定義構(gòu)造函數(shù)可以傳入?yún)?shù)纱皆,做賦值操作時(shí)采用self
調(diào)用屬性以示區(qū)分。
class Person : NSObject {
var name : String?
var age : Int = 0
// 自定義構(gòu)造函數(shù)
init(name:String,age:Int){
self.name = name
self.age = age
}
}
// 調(diào)用自定義的構(gòu)造函數(shù)
let p1 = Person(name: "kaka", age: 12)
print(p1.age)
可以定義字典類型的構(gòu)造函數(shù)芭商。用KVC的方式將字典的值取出來派草,要調(diào)用系統(tǒng)的setValue
方法就必須先調(diào)用系統(tǒng)的構(gòu)造函數(shù)創(chuàng)建出對象。為了防止取出的對象沒有屬性而導(dǎo)致程序奔潰铛楣,需要重寫系統(tǒng)的setValue方法澳眷。
如果用KVC的方式一定要先調(diào)用父類的構(gòu)造函數(shù)。因?yàn)橄到y(tǒng)默認(rèn)調(diào)用是放在方法最后面調(diào)用的蛉艾。
class Person : NSObject {
@objc var name : String?
@objc var age : Int = 0
init(dict:[String : Any]) {
super.init()
// 要調(diào)用系統(tǒng)的`setValue`方法就必須先調(diào)用系統(tǒng)的構(gòu)造函數(shù)創(chuàng)建出對象
setValuesForKeys(dict)
}
// 防止奔潰
override func setValue(_ value: Any?, forUndefinedKey key: String) {
}
}
let p2 = Person(dict:["name":"lala","age":18,"score":33])
p2.name
p2.age
由于swift與objective-c的編譯方式不同,用KVC字典轉(zhuǎn)模型構(gòu)造函數(shù)時(shí)衷敌,需要在屬性前面加上@objc
勿侯。
8、類的屬性監(jiān)聽器
在object-c中缴罗,我們可以重寫set方法來監(jiān)聽屬性的改變助琐,而在swift中也可以通過屬性觀察者來監(jiān)聽和響應(yīng)屬性值的變化。通常用于監(jiān)聽存儲(chǔ)屬性和類屬性的改變面氓。對于計(jì)算屬性則不需要定義屬性觀察者兵钮,因?yàn)槲覀兛梢栽谟?jì)算屬性的setter
中直接觀察并響應(yīng)這種值的變化蛆橡。
可以通過設(shè)置以下觀察方法并響應(yīng)這種值的變化。
willSet
:在屬性值被存儲(chǔ)之前設(shè)置掘譬,此時(shí)新屬性值作為一個(gè)常量參數(shù)被傳入泰演。該參數(shù)名默認(rèn)為newValue
,開發(fā)者可以自己定義該參數(shù)名葱轩。
didSet
:在新屬性值被存儲(chǔ)后立即調(diào)用睦焕,與willSet
不同的是,此時(shí)傳入的是屬性的舊值靴拱,默認(rèn)參數(shù)名為oldValue
垃喊。
上面兩個(gè)方法都只有在屬性第一次被設(shè)置時(shí)才會(huì)調(diào)用,在初始化時(shí)袜炕,不會(huì)去調(diào)用這些監(jiān)聽方法本谜。
class Person : NSObject {
//屬性監(jiān)聽器
var name:String? {
willSet {
print(name as Any)
//如果想要查看接下來的新值,可以使用newValue
print(newValue as Any)
}
didSet {
print(name as Any)
}
}
}
let p = Person()
p.name = "llx"
十四偎窘、閉包
閉包是swift中非常重要的一個(gè)知識點(diǎn)乌助。類似于objective-c中的block,其實(shí)函數(shù)就相當(dāng)于一個(gè)特殊的閉包评架。閉包需要提前寫好眷茁,在適當(dāng)?shù)臅r(shí)候再執(zhí)行。
1纵诞、定義閉包
閉包的格式是(參數(shù)列表)->(返回值類型) in 實(shí)現(xiàn)代碼
舉一個(gè)最簡單的栗子??
用常量記錄一個(gè)代碼塊上祈,按住option
鍵就能看到,b1
是一個(gè)閉包浙芙。再到適合的地方去調(diào)用它登刺。
let b1 = {
print("干掉他們")
}
b1()
再來看一個(gè)帶參數(shù)的閉包。在閉包中嗡呼,參數(shù)纸俭、返回值和實(shí)現(xiàn)代碼都是寫在花括號里面的。in
是用來定義分割和實(shí)現(xiàn)的南窗。
let b2 = {
(x:String)->() in print(x)
}
b2("string")
2揍很、閉包案例
這個(gè)案例要模擬封裝一個(gè)網(wǎng)絡(luò)請求的類。利用閉包將jsonData類型的數(shù)據(jù)傳遞給展示頁面万伤。
- 創(chuàng)建一個(gè)新的項(xiàng)目窒悔,選擇swift語言
- 封裝一個(gè)網(wǎng)絡(luò)請求的類
HttpTool.swift
繼承自NSObject
用異步線程模擬網(wǎng)絡(luò)數(shù)據(jù)請求,再回到主線程中回調(diào)閉包
class HttpTool: NSObject {
//閉包類型:(參數(shù)列表)->(返回值類型)
func loadData(callback:@escaping(_ jsonData : String)->()) {
DispatchQueue.global().async {
print("發(fā)生網(wǎng)絡(luò)請求:\(Thread.current)")
}
DispatchQueue.main.async {
()->Void in
print("獲取到數(shù)據(jù)敌买,并且回調(diào):\(Thread.current)")
callback("jsonData數(shù)據(jù)")
}
}
}
- 到需要接收數(shù)據(jù)的界面定義
Httptool
類的屬性简珠,設(shè)置一個(gè)初始化值,將初始值賦值給變量
在swift中是不需要引入頭文件的虹钮,文件之間可共享
import UIKit
class ViewController: UIViewController {
var tools : HttpTool = HttpTool()
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
//用閉包將json數(shù)據(jù)拿到
tools.loadData { (jsonData) ->() in
print("在viewcontroller中拿到數(shù)據(jù)\(jsonData)" )
}
}
}
3聋庵、尾隨閉包
尾隨閉包用于需要將一個(gè)很長的閉包表達(dá)式作為最后一個(gè)參數(shù)傳遞給函數(shù)膘融。也就是說如果按時(shí)的最后一個(gè)參數(shù)是閉包,那么在調(diào)用它的時(shí)候就可以把這個(gè)閉包寫在括號外面祭玉,并緊跟括號氧映,函數(shù)的其他參數(shù)則仍然寫在括號之中。
//這個(gè)函數(shù)接受一個(gè)String和一個(gè)閉包
//函數(shù)體內(nèi)調(diào)用閉包攘宙,并且將String作為參數(shù)傳遞給閉包
func myFunc(strP:String,closeP:(String)->Void) {
closeP(strP)
}
//普通調(diào)用
myFunc(strP: "hello", closeP: {(string) in print(string)})
//尾隨閉包
myFunc(strP: "hello") {
(string) in print(string)
}
4屯耸、逃逸閉包
當(dāng)一個(gè)閉包作為參數(shù)傳到一個(gè)函數(shù)中,但是該閉包要在函數(shù)返回之后才被執(zhí)行蹭劈,于是就稱這樣的閉包為逃逸閉包疗绣。也就是說閉包逃離了函數(shù)的作用域。寫法是在這個(gè)閉包參數(shù)前加一個(gè)@escaping
用來指明這個(gè)閉包是允許逃逸出該函數(shù)的铺韧。
- 聲明一個(gè)方法多矮,這個(gè)方法是一個(gè)逃逸閉包
該方法要做的事情,就是將閉包添加到數(shù)組中去
//定義數(shù)組哈打,里面的元素都是閉包類型的
var callBackArray : [()->Void] = []
//定義一個(gè)接收閉包的函數(shù)
func testEscapingClosure(callBack:@escaping ()-> Void) {
callBackArray.append(callBack)
}
- 當(dāng)改變數(shù)組的時(shí)候塔逃,取第0個(gè)元素調(diào)用。此時(shí)就改變了變量x的值
class SomeClass {
var x = 10
func doSomething(){
testEscapingClosure {
self.x = 100
}
}
}
let instance = SomeClass()
instance.doSomething()
print(instance.x)
callBackArray.first?()
print(instance.x)
因?yàn)樘右蓍]包是函數(shù)執(zhí)行之后才會(huì)執(zhí)行料仗,所以可以這樣理解:創(chuàng)建一個(gè)類的對象instance
湾盗;在對象中初始化一個(gè)x=10;利用對象執(zhí)行了函數(shù)doSomething
立轧;函數(shù)內(nèi)部調(diào)用全局函數(shù)testEscapingClosure
,期望修改instance對象的x值為100格粪,但是此時(shí)并沒有執(zhí)行這個(gè)包含了賦值語句的閉包。
查找全局?jǐn)?shù)組callBackArray
,找到里面第一個(gè)元素氛改,顯然找到的是在testEscapingClosure
函數(shù)中添加的閉包{self.x = 100}
帐萎,此時(shí)才通過全局?jǐn)?shù)組的查詢找出閉包并執(zhí)行,于是x此時(shí)才被賦值為100胜卤。這就是在函數(shù)執(zhí)行完畢后才執(zhí)行閉包疆导。剛好符合逃逸閉包的定義。
結(jié)論: 逃逸閉包將在函數(shù)執(zhí)行之后執(zhí)行葛躏,于是這段代碼最后輸出為100是因?yàn)殚]包最后才被執(zhí)行……
- 解決循環(huán)引用的三種方式
1澈段、可以使用weak關(guān)鍵字將對象之間的聯(lián)系變?yōu)槿跻?/li>
weak var weakself = self
2、第一種方式的簡化
[weak self]
3舰攒、使用unowned解決
[unowned self]
但是該方法十分危險(xiǎn)均蜜,要確保數(shù)據(jù)一定有值。否則會(huì)發(fā)生奔潰芒率。
__weak 與__unretained有何區(qū)別?
__weak修飾的弱引用篙顺,如果指向的對象被銷毀偶芍,那么指針會(huì)立馬指向nil
__unretained修飾的弱引用充择,如果指向的對象被銷毀,它的指針依然會(huì)指向之前的內(nèi)存地址匪蟀,很容易產(chǎn)生野指針(僵尸對象)
十五椎麦、tableView的用法
1、 懶加載
swift中也有懶加載的方式材彪,并且在swift中有專門的關(guān)鍵字lazy
來實(shí)現(xiàn)某一個(gè)屬性實(shí)現(xiàn)懶加載观挎。
格式:lazy var 變量:類型 = {創(chuàng)建變量代碼}()
懶加載的本質(zhì)在第一次使用的時(shí)候執(zhí)行閉包,將閉包的返回值賦值給屬性段化,并且只會(huì)賦值一次嘁捷。
//懶加載只能用于結(jié)構(gòu)體或者類的成員變量中
class Person:NSObject {
lazy var array : [String] = {
()->[String] in
return ["llx","lll"]
}()
}
2、tableView的使用
使用步驟如下:
- 創(chuàng)建tableView對象
使用懶加載的方式显熏,到需要用到的時(shí)候再創(chuàng)建tableView雄嚣。將tableView添加到控制器上的View。
class ViewController: UIViewController {
lazy var tableView:UITableView = UITableView()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(tableView)
}
}
- 設(shè)置tableView的frame
tableView.frame = view.bounds
- 設(shè)置數(shù)據(jù)源和代理
實(shí)現(xiàn)UITableView的協(xié)議喘蟆,并為tableView設(shè)置數(shù)據(jù)源
class ViewController: UIViewController ,UITableViewDataSource,UITableViewDelegate{
lazy var tableView:UITableView = UITableView()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(tableView)
tableView.frame = view.bounds
//設(shè)置數(shù)據(jù)源
tableView.dataSource = self
tableView.delegate = self
}
}
- 實(shí)現(xiàn)代理方法
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 20
}
創(chuàng)建cell缓升。因?yàn)閏ell是個(gè)可選類型,有可能有值蕴轨,也可能為nil港谊。所以要進(jìn)行判斷。給cell設(shè)置數(shù)據(jù)的時(shí)候橙弱,選擇textLabel點(diǎn)擊option
會(huì)發(fā)現(xiàn)textLabel也是可選類型歧寺。
在最后返回cell的時(shí)候,對cell進(jìn)行強(qiáng)制解包膘螟。因?yàn)橹耙呀?jīng)做過判斷成福,所以不會(huì)出現(xiàn)程序奔潰的問題。
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let CellID = "CellID"
var cell = tableView.dequeueReusableCell(withIdentifier: CellID)
if cell == nil {
cell = UITableViewCell(style: .default, reuseIdentifier: CellID)
}
cell?.textLabel?.text = "測試數(shù)據(jù):\(indexPath.row)"
return cell!
}
實(shí)現(xiàn)點(diǎn)擊的代理方法
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("點(diǎn)擊了:\(indexPath.row)")
}
十六荆残、swift中的注釋
在swift中奴艾,類似于paramg --mark
的寫法是不可行的。
它是如下兩種形式
//MARK:- 要寫的內(nèi)容
用于分組
class ViewController: UIViewController ,UITableViewDataSource,UITableViewDelegate{
// MARK:- 懶加載
lazy var tableView:UITableView = UITableView()
// MARK:- 系統(tǒng)回調(diào)函數(shù)
override func viewDidLoad() {
super.viewDidLoad()
}
}
這樣寫的話内斯,就可以在菜單欄看到分組的信息
/// 提示信息
用于提示
若在tableView系列的某個(gè)方法上面寫上///提示
蕴潦,到其他地方調(diào)用該方法時(shí),會(huì)出現(xiàn)前面寫的注釋信息俘闯。
十七潭苞、枚舉
1、定義
在swift中真朗,枚舉使用的是由enum
關(guān)鍵字來創(chuàng)建的枚舉此疹,枚舉的所有成員都放在一對大括號里面。它為一組相關(guān)的值定義一個(gè)共同的類型。使用case
關(guān)鍵字來定義一個(gè)新的枚舉成員值蝗碎。
enum SomeEnum {
// 在這里定義枚舉
case north
case south
case east
case west
}
上面這個(gè)枚舉定義的東南西北四個(gè)值就是這個(gè)枚舉的成員值湖笨。與C語言和objective-c不同的是,swift的枚舉成員值在創(chuàng)建的時(shí)候并不會(huì)被賦予一個(gè)默認(rèn)的整形值蹦骑。這些值的類型就是剛剛定義好的枚舉的名字SomeEnum
慈省。
如果希望多個(gè)成員值要寫在同一行中,可以使用逗號將他們分割開眠菇。
enum Plant {
case mercury,earth,mars
}
每個(gè)枚舉都定義了一個(gè)新的類型边败,就像swift中的其他類型一樣。此時(shí)可以把它賦值給一個(gè)變量捎废,而且可以用點(diǎn)語法這種形式調(diào)用笑窜。
var directionT = SomeEnumeration.west
directionT = .east
注意:在switch中使用枚舉值的時(shí)候,一定要窮舉出所有的情況缕坎,如果忽略其中的一個(gè)怖侦,代碼都無法編譯通過。因?yàn)樗鼪]有考慮到枚舉類的全部成員谜叹。如果說不需要匹配所有的枚舉成員匾寝,可以提供一個(gè)default分支來涵蓋其他未明確處理的枚舉成員。
class Person:NSObject{
var directionT = SomeEnum.west
func direc() {
switch directionT {
case .north:
print("north")
case .east:
print("east")
default:
print("沒有方向")
}
}
}
2荷腊、關(guān)聯(lián)值
可以定義swift的枚舉類存儲(chǔ)任意類型的關(guān)聯(lián)值艳悔,而且每個(gè)枚舉成員的關(guān)聯(lián)值類型都可以不相同。比如說女仰,來創(chuàng)建一個(gè)條形碼類型猜年。類似于庫存哼审,可以有不同類型的條形碼去識別商品擂涛,比如說通過數(shù)字图云,或者根據(jù)產(chǎn)品代碼來識別慰照。
enum BarCode {
case upc(Int,Int,Int,Int)
case qrCode(String)
}
上面代碼可以理解為定義一個(gè)名為BarCode
的枚舉類型。它的一個(gè)成員值是一個(gè)具有(Int,Int,Int,Int)類型關(guān)聯(lián)值的upc
廊驼,另一個(gè)成員值是具有String類型的qrCode
之后可以使用任意的條形碼類型去創(chuàng)建新的條形碼
class Person:NSObject {
// 創(chuàng)建一個(gè)名為pBar變量嫌拣,并將Barcode.upc賦值給它恢口。
func function() {
var pBar = BarCode.upc(9, 0, 3, 3)
pBar = .qrCode("ABCD")
}
}
這個(gè)時(shí)候原來的barcode.upc和其整數(shù)關(guān)聯(lián)值被新的Barcode.qrCode和其字符串關(guān)聯(lián)值所替代了聂渊。
3差购、枚舉的原始值
枚舉的原始值就是枚舉的默認(rèn)值,這些原始值的類型必須相同汉嗽。在定義枚舉的時(shí)候必須給出類型欲逃。
enum ASCIICHAR : Character {
case tab = "\t"
case lineFeed = "\n"
case carriageReturn = "\r"
}
在使用原始值為整數(shù)或者字符串類型的枚舉時(shí),不需要顯式的為每一個(gè)枚舉成員設(shè)置原始值饼暑,swift將會(huì)自動(dòng)未它們賦值稳析。
enum Planet : Int {
case mercury = 1, venus,earth,mars
}
上面這個(gè)例子洗做,Planet.mercury
原始值是1,那么后面的venus
就是2迈着,之后以此類推竭望。
可以通過rawValue
屬性來訪問枚舉變量的原始值.
let earthsOrder = Planet.earth.rawValue
4、枚舉遞歸
枚舉成員的關(guān)聯(lián)值為當(dāng)前枚舉類型時(shí)稱為遞歸枚舉裕菠。那我們可以通過使用indirect
修飾枚舉變量。indirect
修飾整個(gè)枚舉時(shí),所有成員均可遞歸(也可不遞歸??)闭专。
indirect enum Ari {
case number(Int)
case addition(Ari,Ari)
case multi(Ari,Ari)
}
上面定義的枚舉類型可以存儲(chǔ)三種算術(shù)表達(dá)式:純數(shù)字奴潘、兩個(gè)表達(dá)式相加、兩個(gè)表達(dá)式相乘影钉。
let five = Ari.number(5)
let four = Ari.number(4)
let sum = Ari.addition(five, four)
let product = Ari.multi(sum, Ari.number(2))
通過枚舉遞歸画髓,就成功的創(chuàng)建了一個(gè)(5+4)*2的式子。
十八平委、結(jié)構(gòu)體
結(jié)構(gòu)體通過struct
去聲明奈虾。在swift中,用到了大量的結(jié)構(gòu)體廉赔,比如說基本的數(shù)據(jù)類型都是結(jié)構(gòu)體而不是類肉微。這意味著它們被賦值給新的常量或者變量,或者被傳入函數(shù)或方法中時(shí)蜡塌,值會(huì)被拷貝碉纳。
struct teacher {
var name : String = ""
var age : Int = 30
}
十九、擴(kuò)展
擴(kuò)展 (Extension)可以做到無需修改原本的代碼就直接把想要的功能實(shí)現(xiàn)馏艾。
extension 某個(gè)現(xiàn)有的class {
//添加新功能
}
限制:
- 不能添加任何已存在的 法或是屬性
- 添加的屬性不能是存儲(chǔ)屬性劳曹,只能是計(jì)算屬性
1、擴(kuò)展在方法中的應(yīng)用
extension String {
func sayHello() {
print("Hello from extension")
}
}
上面這段代碼是對String
做了一個(gè)擴(kuò)展琅摩。之后聲明一個(gè)變量調(diào)用擴(kuò)展方法铁孵。
var hello = "hi"
hello.sayHello()
此后,任何String
類型都可以調(diào)用該擴(kuò)展方法房资。
2蜕劝、用擴(kuò)展進(jìn)行計(jì)算
extension Int {
var squared : Int {
return (self * self)
}
}
上面這段代碼對Int
擴(kuò)展了一個(gè)屬性,讓它計(jì)算一個(gè)數(shù)字的平方值志膀。
var newInt = 30
newInt.squared
999.squared
3熙宇、擴(kuò)展類或結(jié)構(gòu)體
- 創(chuàng)建一個(gè)普通類
class Lisa {
var lisa = "半邊天使"
}
- 對類擴(kuò)展,新增一個(gè)方法溉浙,使其能做自我介紹
extension Lisa {
func describe() -> String {
return "我可是會(huì)傲嬌的"
}
}
- 創(chuàng)建對象調(diào)用方法
二十烫止、泛型
泛型可以讓開發(fā)者寫出靈活可重復(fù)使用的方法跟結(jié)構(gòu)。
先看一個(gè)栗子??4粱馆蠕!
var stringArray = ["Hi", "Hello", "Bye"]
var intArray = [1,2,3]
var doubleArray = [1.1,2.2,3.3]
上面創(chuàng)建了三個(gè)不同類型的數(shù)組期升,若是要求打印所有數(shù)組中的元素,通常會(huì)怎么做呢互躬?
func printStringFromArray(a: [String]) {
for s in a {
print(s) }
}
func printIntFromArray(a: [Int]){
for i in a {
print(i) }
}
func printdoubleFromArray(a:[Double]) {
for d in a {
print(d) }
}
printStringFromArray(a: stringArray)
printIntFromArray(a: intArray)
printdoubleFromArray(a: doubleArray)
上面這段冗長的代碼實(shí)在讓人不忍直視播赁。而泛型的出現(xiàn)正好可以解決這一問題。
func printEelementFormArray<T>(a:[T]){
for element in a {
print(element)
}
}
這段代碼中的T
代表了任意的元素吼渡。無論上面類型的數(shù)據(jù)都能放入其中容为。之后只要調(diào)用者一個(gè)方法,傳入不同的數(shù)組就能將不同類型的元素打印出來寺酪。
二十一坎背、協(xié)議
1、對面向?qū)ο笳Z言的吐槽
- 使用子類時(shí)寄雀,協(xié)議繼承父類的屬性和方法得滤。其中某些方法或?qū)傩圆⒉皇情_發(fā)者所需要的。這會(huì)讓代碼變得異常的臃腫盒犹。
- 若一個(gè)類擁有很多父類懂更,會(huì)讓開發(fā)者很難找到每個(gè)類中的問題并進(jìn)行修改。
- 對象引用到內(nèi)存的同一地方急膀,若是發(fā)生改變沮协,可能會(huì)造成代碼混亂的現(xiàn)象。
而swift是一種面向協(xié)議的語言脖阵。協(xié)議其實(shí)就像籃球教練皂股,會(huì)告訴選手如何去訓(xùn)練,但是教練本身并不會(huì)出現(xiàn)在球場命黔。Swift中的protocol不僅能定義方法還能定義屬性呜呐,配合extension擴(kuò)展的使用還能提供一些方法的默認(rèn)實(shí)現(xiàn),而且不僅類可以遵循協(xié)議悍募,現(xiàn)在的枚舉和結(jié)構(gòu)體也能遵循協(xié)議了蘑辑。
2、一個(gè)簡單的協(xié)議案例
- 創(chuàng)建一個(gè)簡單的協(xié)議坠宴,并讓一個(gè)結(jié)構(gòu)體去遵循
遵循協(xié)議的方法與繼承類似洋魂。
protocol People {
}
struct Lisa: People {
}
- 完善協(xié)議
給協(xié)議添加一些屬性和方法,用get
set
設(shè)定協(xié)議的狀態(tài)喜鼓。遵循協(xié)議時(shí)要了解變量是否能讀取或賦值副砍。
protocol People {
var name: String {get set}
var race: String {get set}
func sayHi()
}
- 在結(jié)構(gòu)體中實(shí)現(xiàn)協(xié)議的方法和變量
struct Lisa: People {
var name: String = "Lisa"
var race: String = "Asian"
func sayHi() {
print("Hi~, I'm \(name)")
}
}
3、協(xié)議的繼承
- 創(chuàng)建一個(gè)協(xié)議庄岖,讓該協(xié)議繼承自之前創(chuàng)建的People協(xié)議
protocol superman {
var canFly: Bool {get set}
func punch()
}
protocol superman: People {
var canFly: Bool {get set}
func punch()
}
- 調(diào)用
struct AngleLisa: superman {
var name: String = "Lisa"
var race: String = "Asian"
func sayHi() {
print("Hi, I'm \(name)")
}
var canFly: Bool = true
func punch() {
print("punch Vergil")
}
}
由此可知豁翎,一旦協(xié)議進(jìn)行了繼承,不但要實(shí)現(xiàn)本協(xié)議中所聲明的方法和屬性隅忿,連協(xié)議父類的方法和屬性也不能落下心剥。
二十二邦尊、swift4新特性
以下內(nèi)容來自 最全的 Swift 4 新特性解析
感謝大佬提供學(xué)習(xí)資源!S派铡蝉揍!
1、語法改進(jìn)
- 在擴(kuò)展extension中可以訪問private的屬性
舉一個(gè)簡單的栗子??畦娄!
struct Date: Equatable, Comparable {
private let secondsSinceReferenceDate: Double
static func ==(lhs: Date, rhs: Date) -> Bool {
return lhs.secondsSinceReferenceDate == rhs.secondsSinceReferenceDate
}
static func <(lhs: Date, rhs: Date) -> Bool {
return lhs.secondsSinceReferenceDate < rhs.secondsSinceReferenceDate
}
}
上面代碼定義了一個(gè) Date 結(jié)構(gòu)體又沾,并實(shí)現(xiàn) Equatable 和 Comparable 協(xié)議。為了讓代碼更清晰熙卡,可讀性更好捍掺,一般會(huì)把對協(xié)議的實(shí)現(xiàn)放在單獨(dú)的 extension 中,這也是一種非常符合 Swift 風(fēng)格的寫法再膳,如下:
struct Date {
private let secondsSinceReferenceDate: Double
}
extension Date: Equatable {
static func ==(lhs: Date, rhs: Date) -> Bool {
return lhs.secondsSinceReferenceDate == rhs.secondsSinceReferenceDate
}
}
extension Date: Comparable {
static func <(lhs: Date, rhs: Date) -> Bool {
return lhs.secondsSinceReferenceDate < rhs.secondsSinceReferenceDate
}
}
但是在 Swift 3 中,這樣寫會(huì)導(dǎo)致編譯報(bào)錯(cuò)曲横,extension 中無法獲取到 secondsSinceReferenceDate
屬性喂柒,因?yàn)樗?private 的。于是在 Swift 3 中禾嫉,必須把 private
改為 fileprivate
灾杰。
struct Date {
fileprivate let secondsSinceReferenceDate: Double
}
...
但是如果用 fileprivate
,屬性的作用域就會(huì)比我們需要的更大熙参,可能會(huì)不小心造成屬性的濫用艳吠。
在 Swift 4 中,private 的屬性的作用域擴(kuò)大到了 extension 中孽椰,并且被限定在了 struct 和 extension 內(nèi)部昭娩,這樣struct的屬性就不需要再用 fileprivate
修飾了,這是最好的結(jié)果黍匾。
- 類型和協(xié)議的組合類型
protocol Shakeable {
func shake()
}
extension UIButton: Shakeable { func shake() {/* */ } }
extension UISlider: Shakeable { func shake() {/* */ } }
func shakeEm(controls: [???]) {
for control in controls where control.state.isEnabled {
}
control.shake()
}
仔細(xì)思考上面的代碼栏渺,如果是swift3中,func shakeEm(controls: [???])
中的???
應(yīng)該寫上面類型呢锐涯?如果寫UIControl
磕诊,那么調(diào)用control.shake()
時(shí)就會(huì)報(bào)錯(cuò)。如果寫Shakeable
類型纹腌,那么control.state.isEnabled
這條語句就會(huì)報(bào)錯(cuò)霎终。
swift4為了解決類似問題,實(shí)現(xiàn)了把類型和協(xié)議用&
組合在一起作為一個(gè)類型使用的寫法升薯。把它聲明為UIControl & Shakeable
類型莱褒。
func shakeEm(controls: [UIControl & Shakeable]) {
for control in controls where control.isEnabled {
control.shake()
}
}
- Associated Type 可以追加 Where 約束語句
在 Swift 4 中可以在 associatedtype
后面聲明的類型后追加 where 語句。
protocol Sequence {
associatedtype Element where Self.Element == Self.Iterator.Element
// ...
}
它限定了 Sequence 中 Element 這個(gè)類型必須和 Iterator.Element 的類型一致覆劈。
通過 where 語句可以對類型添加更多的約束保礼,使其更嚴(yán)謹(jǐn)沛励,避免在使用這個(gè)類型時(shí)做多余的類型判斷。
- 新的 Key Paths 語法
先來看看 Swift 3 中 Key Paths 的寫法:
@objcMembers class Kid: NSObject {
dynamic var nickname: String = ""
dynamic var age: Double = 0.0
dynamic var friends: [Kid] = []
}
var ben = Kid(nickname: "Benji", age: 5.5)
let kidsNameKeyPath = #keyPath(Kid.nickname)
let name = ben.valueForKeyPath(kidsNameKeyPath)
ben.setValue("Ben", forKeyPath: kidsNameKeyPath)
Swift 4 中創(chuàng)建一個(gè) KeyPath
用 \
作為開頭:
\Kid.nickname
當(dāng)編譯器可以推導(dǎo)出類型時(shí)炮障,可以省略基礎(chǔ)類型部分:
\.nickname
上面的代碼在 Swift 4 中就可以這樣寫:
struct Kid {
var nickname: String = ""
var age: Double = 0.0
var friends: [Kid] = []
}
var ben = Kid(nickname: "Benji", age: 8, friends: [])
let name = ben[keyPath: \Kid.nickname]
ben[keyPath: \Kid.nickname] = "BigBen"
相比 Swift 3目派,Swift 4 的 Key Paths 具有以下優(yōu)勢:
類型可以定義為 class、struct
定義類型時(shí)無需加上 @objcMembers胁赢、dynamic 等關(guān)鍵字
性能更好
類型安全和類型推斷企蹭,例如 ben.valueForKeyPath(kidsNameKeyPath) 返回的類型是 Any,ben[keyPath: \Kid.nickname] 直接返回 String 類型
可以在所有值類型上使用
- 下標(biāo)支持泛型
Swift 支持通過下標(biāo)來讀寫容器中的數(shù)據(jù)智末,但是如果容器類中的數(shù)據(jù)類型定義為泛型谅摄,以前的下標(biāo)語法就只能返回 Any,在取出值后需要用 as? 來轉(zhuǎn)換類型系馆。Swift 4 定義下標(biāo)也可以使用泛型了送漠。但是并不需要做轉(zhuǎn)型操作。
struct GenericDictionary<Key: Hashable, Value> {
private var data: [Key: Value]
init(data: [Key: Value]) {
self.data = data
}
subscript<T>(key: Key) -> T? {
return data[key] as? T
}
}
let dictionary = GenericDictionary(data: ["Name": "Xiaoming"])
let name: String? = dictionary["Name"] // 不需要再寫 as? String
2由蘑、字符串
- Unicode 字符串在計(jì)算 count 時(shí)的正確性改善
在 Unicode 中闽寡,有些字符是由幾個(gè)其它字符組成的,比如 é 這個(gè)字符尼酿,它可以用 \u{E9} 來表示爷狈,也可以用 e 字符和上面一撇字符組合在一起表示 \u{65}\u{301}。
var family = "??"
family += "\u{200D}??"
family += "\u{200D}??"
family += "\u{200D}??"
print(family)
print(family.characters.count)
這個(gè) family 是一個(gè)由多個(gè)字符組合成的字符裳擎,打印出來的結(jié)果為 ???????????涎永。上面的代碼在 Swift 3 中打印的 count 數(shù)是 4,在 Swift 4 中打印出的 count 是 1鹿响。
- 更快的處理速度
Swift 4 的字符串優(yōu)化了底層實(shí)現(xiàn)羡微,對于英語、法語抢野、德語拷淘、西班牙語的處理速度提高了 3.5 倍。對于簡體中文指孤、日語的處理速度提高了 2.5 倍启涯。
去掉了characters
One-sided Slicing
Swift 4 新增了一個(gè)語法糖 ...
可以對字符串進(jìn)行單側(cè)邊界取子串。
Swift 3:
let values = "abcdefg"
let startSlicingIndex = values.index(values.startIndex, offsetBy: 3)
let subvalues = values[startSlicingIndex..<values.endIndex]
// defg
Swift 4:
let values = "abcdefg"
let startSlicingIndex = values.index(values.startIndex, offsetBy: 3)
let subvalues = values[startSlicingIndex...] // One-sided Slicing
// defg
- String 當(dāng)做 Collection 來用
Swift 4 中 String 可以當(dāng)做 Collection 來用恃轩,并不是因?yàn)?String 實(shí)現(xiàn)了 Collection 協(xié)議结洼,而是 String 本身增加了很多 Collection 協(xié)議中的方法,使得 String 在使用時(shí)看上去就是個(gè) Collection叉跛。例如:
翻轉(zhuǎn)字符串:
let abc: String = "abc"
print(String(abc.reversed()))
// cba
遍歷字符:
let abc: String = "abc"
for c in abc {
print(c)
}
/*
a
b
c
*/
Map松忍、Filter、Reduce:
// map
let abc: String = "abc"
_ = abc.map {
print($0.description)
}
// filter
let filtered = abc.filter { $0 == "b" }
// reduce
let result = abc.reduce("1") { (result, c) -> String in
print(result)
print(c)
return result + String(c)
}
print(result)
- Substring
在 Swift 中筷厘,String 的背后有個(gè) Owner Object 來跟蹤和管理這個(gè) String鸣峭,String 對象在內(nèi)存中的存儲(chǔ)由內(nèi)存其實(shí)地址宏所、字符數(shù)、指向 Owner Object 指針組成摊溶。Owner Object 指針指向 Owner Object 對象爬骤,Owner Object 對象持有 String Buffer。當(dāng)對 String 做取子字符串操作時(shí)莫换,子字符串的 Owner Object 指針會(huì)和原字符串指向同一個(gè)對象霞玄,因此子字符串的 Owner Object 會(huì)持有原 String 的 Buffer。當(dāng)原字符串銷毀時(shí)拉岁,由于原字符串的 Buffer 被子字符串的 Owner Object 持有了坷剧,原字符串 Buffer 并不會(huì)釋放,造成極大的內(nèi)存浪費(fèi)喊暖。
在 Swift 4 中惫企,做取子串操作的結(jié)果是一個(gè) Substring 類型,它無法直接賦值給需要 String 類型的地方陵叽。必須用 String(<substring>) 包一層雅任,系統(tǒng)會(huì)通過復(fù)制創(chuàng)建出一個(gè)新的字符串對象,這樣原字符串在銷毀時(shí)咨跌,原字符串的 Buffer 就可以完全釋放了。
let big = downloadHugeString()
let small = extractTinyString(from: big)
mainView.titleLabel.text = small // Swift 4 編譯報(bào)錯(cuò)
mainView.titleLabel.text = String(small) // 編譯通過
- 多行字符串字面量
Swift 3 中寫很長的字符串只能寫在一行硼婿。
func tellJoke(name: String, character: Character) {
let punchline = name.filter { $0 != character }
let n = name.count - punchline.count
let joke = "Q: Why does \(name) have \(n) \(character)'s in their name?\nA: I don't know, why does \(name) have \(n) \(character)'s in their name?\nQ: Because otherwise they'd be called \(punchline)."
print(joke)
}
tellJoke(name: "Edward Woodward", character: "d")
字符串中間有換行只能通過添加 \n 字符來代表換行锌半。
Swift 4 可以把字符串寫在一對 """ 中,這樣字符串就可以寫成多行寇漫。
func tellJoke(name: String, character: Character) {
let punchline = name.filter { $0 != character }
let n = name.count - punchline.count
let joke = """
Q: Why does \(name) have \(n) \(character)'s in their name?
A: I don't know, why does \(name) have \(n) \(character)'s in their name?
Q: Because otherwise they'd be called \(punchline).
"""
print(joke)
}
tellJoke(name: "Edward Woodward", character: "d")
3刊殉、Swift 標(biāo)準(zhǔn)庫
- Encoding and Decoding
當(dāng)需要將一個(gè)對象持久化時(shí),需要把這個(gè)對象序列化州胳,往常的做法是實(shí)現(xiàn) NSCoding 協(xié)議记焊,寫過的人應(yīng)該都知道實(shí)現(xiàn) NSCoding 協(xié)議的代碼寫起來很痛苦,尤其是當(dāng)屬性非常多的時(shí)候栓撞。幾年前有一個(gè)工具能自動(dòng)生成 Objective-C 的實(shí)現(xiàn) NSCoding 協(xié)議代碼遍膜,當(dāng)時(shí)用著還不錯(cuò),但后來這個(gè)工具已經(jīng)沒有人維護(hù)很久了瓤湘,而且不支持 Swift瓢颅。
Swift 4 中引入了 Codable
幫我們解決了這個(gè)問題。
struct Language: Codable {
var name: String
var version: Int
}
我們想將這個(gè) Language 對象的實(shí)例持久化弛说,只需要讓 Language 符合 Codable 協(xié)議即可挽懦,Language 中不用寫別的代碼。符合了 Codable 協(xié)議以后木人,可以選擇把對象 encode 成 JSON 或者 PropertyList信柿。
Encode 操作如下:
let swift = Language(name: "Swift", version: 4)
if let encoded = try? JSONEncoder().encode(swift) {
// 把 encoded 保存起來
}
Decode 操作如下:
if let decoded = try? JSONDecoder().decode(Language.self, from: encoded) {
print(decoded.name)
}
- Sequence 改進(jìn)
Swift 3:
protocol Sequence {
associatedtype Iterator: IteratorProtocol
func makeIterator() -> Iterator
}
Swift 4:
protocol Sequence {
associatedtype Element
associatedtype Iterator: IteratorProtocol where Iterator.Element == Element
func makeIterator() -> Iterator
}
由于 Swift 4 中的 associatedtype
支持追加 where 語句冀偶,所以 Sequence
做了這樣的改進(jìn)。
Swift 4 中獲取 Sequence
的元素類型可以不用 Iterator.Element
渔嚷,而是直接取 Element
进鸠。
SubSequence 也做了修改:
protocol Sequence {
associatedtype SubSequence: Sequence
where SubSequence.SubSequence == SubSequence,
SubSequence.Element == Element
}
通過 where 語句的限定,保證了類型正確圃伶,避免在使用 Sequence 時(shí)做一些不必要的類型判斷堤如。
Collection 也有一些類似的修改。
- Protocol-oriented integers
整數(shù)類型符合的協(xié)議有修改窒朋,新增了 FixedWidthInteger 等協(xié)議搀罢,具體的協(xié)議繼承關(guān)系如下:
+-------------+ +-------------+
+------>+ Numeric | | Comparable |
| | (+,-,*) | | (==,<,>,...)|
| +------------++ +---+---------+
| ^ ^
+-------+------------+ | |
| SignedNumeric | +-+-------+-----------+
| (unary -) | | BinaryInteger |
+------+-------------+ |(words,%,bitwise,...)|
^ ++---+-----+----------+
| +-----------^ ^ ^---------------+
| | | |
+------+---------++ +---------+---------------+ +--+----------------+
| SignedInteger | | FixedWidthInteger | | UnsignedInteger |
| | |(endianness,overflow,...)| | |
+---------------+-+ +-+--------------------+--+ +-+-----------------+
^ ^ ^ ^
| | | |
| | | |
++--------+-+ +-+-------+-+
|Int family |-+ |UInt family|-+
+-----------+ | +-----------+ |
+-----------+ +-----------+
- Dictionary and Set enhancements
這里簡單列一下 Dictionary 和 Set 增強(qiáng)了哪些功能:
通過 Sequence 來初始化
可以包含重復(fù)的 Key
Filter 的結(jié)果的類型和原類型一致
Dictionary 的 mapValues 方法
Dictionary 的默認(rèn)值
Dictionary 可以分組
Dictionary 可以翻轉(zhuǎn)
- NSNumber bridging and Numeric types
let n = NSNumber(value: 999)
let v = n as? UInt8 // Swift 4: nil, Swift 3: 231
在 Swift 4 中,把一個(gè)值為 999 的 NSNumber 轉(zhuǎn)換為 UInt8 后侥猩,能正確的返回 nil榔至,而在 Swift 3 中會(huì)不可預(yù)料的返回 231。
- MutableCollection.swapAt(::)
MutableCollection 現(xiàn)在有了一個(gè)新方法 swapAt(::) 用來交換兩個(gè)位置的值欺劳,例如:
var mutableArray = [1, 2, 3, 4]
mutableArray.swapAt(1, 2)
print(mutableArray)
// 打印結(jié)果:[1, 3, 2, 4]
4唧取、構(gòu)建過程改進(jìn)
- New Build System
Xcode 9 引入了 New Build System,可在 Xcode 9 的 File -> Project Settings... 中選擇開啟划提。
- 預(yù)編譯 Bridging Headers 文件
對于 Swift 和 Objective-C 混合的項(xiàng)目枫弟,Swift 調(diào)用 Objective-C 時(shí),需要建立一個(gè) Bridging Headers 文件鹏往,然后把 Swift 要調(diào)用的 Objective-C 類的頭文件都寫在里面淡诗,編譯器會(huì)讀取 Bridging Headers 中的頭文件,然后生成一個(gè)龐大的 Swift 文件伊履,文件內(nèi)容是這些頭文件內(nèi)的 API 的 Swift 版本韩容。然后編譯器會(huì)在編譯每一個(gè) Swift 文件時(shí),都要編譯一遍這個(gè)龐大的 Swift 文件的內(nèi)容唐瀑。
有了預(yù)編譯 Bridging Headers 以后群凶,編譯器會(huì)在預(yù)編譯階段把 Bridging Headers 編譯一次,然后插入到每個(gè) Swift 文件中哄辣,這樣就大大提高了編譯速度请梢。
蘋果宣稱 Xcode 9 和 Swift 4 對于 Swift 和 Objective-C 混合編譯的速度提高了 40%
- Indexing 可以在編譯的同時(shí)進(jìn)行
用 Swift 開發(fā)項(xiàng)目時(shí),近幾個(gè)版本的 Xcode 進(jìn)行 Indexing 的速度慢的令人發(fā)指力穗。Xcode 9 和 Swift 4 在這方面做了優(yōu)化溢陪,可以在編譯的同時(shí)進(jìn)行 Indexing,一般編譯結(jié)束后 Indexing 也會(huì)同時(shí)完成睛廊。
- COW Existential Containers
Swift 中有個(gè)東西叫 Existential Containers形真,它用來保存未知類型的值,它的內(nèi)部是一個(gè) Inline value buffer,如果 Inline value buffer 中的值占用空間很大時(shí)咆霜,這個(gè)值會(huì)被分配在堆上邓馒,然而在堆上分配內(nèi)存是一個(gè)性能比較慢的操作。
Swift 4 中為了優(yōu)化性能引入了 COW Existential Containers蛾坯,這里的 COW 就代表 "Copy-On-Write"光酣,當(dāng)存在多個(gè)相同的值時(shí),他們會(huì)共用 buffer 上的空間脉课,直到某個(gè)值被修改時(shí)救军,這個(gè)被修改的值才會(huì)被拷貝一份并分配內(nèi)存空間
- 移除未調(diào)用的協(xié)議實(shí)現(xiàn)
struct Date {
private let secondsSinceReferenceDate: Double
}
extension Date: Equatable {
static func ==(lhs: Date, rhs: Date) -> Bool {
return lhs.secondsSinceReferenceDate == rhs.secondsSinceReferenceDate
}
}
extension Date: Comparable {
static func <(lhs: Date, rhs: Date) -> Bool {
return lhs.secondsSinceReferenceDate < rhs.secondsSinceReferenceDate
}
}
看上面例子,Date 實(shí)現(xiàn)了 Equatable 和 Comparable 協(xié)議倘零。編譯時(shí)如果編譯器發(fā)現(xiàn)沒有任何地方調(diào)用了對 Date 進(jìn)行大小比較的方法唱遭,編譯器會(huì)移除 Comparable 協(xié)議的實(shí)現(xiàn),來達(dá)到減小包大小的目的呈驶。
- 減少隱式 @objc 自動(dòng)推斷
在項(xiàng)目中想把 Swift 寫的 API 暴露給 Objective-C 調(diào)用拷泽,需要增加 @objc。在 Swift 3 中袖瞻,編譯器會(huì)在很多地方為我們隱式的加上 @objc司致,例如當(dāng)一個(gè)類繼承于 NSObject,那么這個(gè)類的所有方法都會(huì)被隱式的加上 @objc聋迎。
class MyClass: NSObject {
func print() { ... } // 包含隱式的 @objc
func show() { ... } // 包含隱式的 @objc
}
這樣很多并不需要暴露給 Objective-C 也被加上了 @objc脂矫。大量 @objc 會(huì)導(dǎo)致二進(jìn)制文件大小的增加。
在 Swift 4 中霉晕,隱式 @objc 自動(dòng)推斷只會(huì)發(fā)生在很少的當(dāng)必須要使用 @objc 的情況羹唠,比如:
復(fù)寫父類的 Objective-C 方法
符合一個(gè) Objective-C 的協(xié)議
其它大多數(shù)地方必須手工顯示的加上 @objc。
減少了隱式 @objc 自動(dòng)推斷后娄昆,Apple Music app 的包大小減少了 5.7%。
5缝彬、 Exclusive Access to Memory
在遍歷一個(gè) Collection 的時(shí)候可以去修改每一個(gè)元素的值萌焰,但是在遍歷時(shí)如果去添加或刪除一個(gè)元素就可能會(huì)引起 Crash。
例如為 MutableCollection 擴(kuò)展一個(gè) modifyEach 方法來修改每個(gè)元素的值谷浅,代碼如下:
extension MutableCollection {
mutating func modifyEach(_ body: (inout Element) -> ()) {
for index in self.indices {
body(&self[index])
}
}
}
假如在調(diào)用 modifyEach 時(shí)去刪除元素:
var numbers = [1, 2, 3]
numbers.modifyEach { element in
element *= 2
numbers.removeAll()
}
就會(huì)在運(yùn)行時(shí) Crash扒俯。Swift 4 中引入了 Exclusive Access to Memory,使得這個(gè)錯(cuò)誤可以在編譯時(shí)被檢查出來一疯。