swift4.0語法雜記(精簡版)

小視頻

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í)被檢查出來一疯。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末撼玄,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子墩邀,更是在濱河造成了極大的恐慌掌猛,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件眉睹,死亡現(xiàn)場離奇詭異荔茬,居然都是意外死亡废膘,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進(jìn)店門慕蔚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來丐黄,“玉大人,你說我怎么就攤上這事孔飒」喙耄” “怎么了?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵坏瞄,是天一觀的道長桂对。 經(jīng)常有香客問我,道長惦积,這世上最難降的妖魔是什么接校? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮狮崩,結(jié)果婚禮上蛛勉,老公的妹妹穿的比我還像新娘。我一直安慰自己睦柴,他們只是感情好诽凌,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著坦敌,像睡著了一般侣诵。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上狱窘,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天杜顺,我揣著相機(jī)與錄音,去河邊找鬼蘸炸。 笑死躬络,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的搭儒。 我是一名探鬼主播穷当,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼淹禾!你這毒婦竟也來了馁菜?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤铃岔,失蹤者是張志新(化名)和其女友劉穎汪疮,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡铲咨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年躲胳,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片纤勒。...
    茶點(diǎn)故事閱讀 38,100評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡坯苹,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出摇天,到底是詐尸還是另有隱情粹湃,我是刑警寧澤,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布泉坐,位于F島的核電站为鳄,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏腕让。R本人自食惡果不足惜孤钦,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望纯丸。 院中可真熱鬧偏形,春花似錦、人聲如沸觉鼻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽坠陈。三九已至萨惑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間仇矾,已是汗流浹背庸蔼。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留贮匕,地道東北人姐仅。 一個(gè)月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像粗合,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子乌昔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評論 2 345

推薦閱讀更多精彩內(nèi)容

  • importUIKit classViewController:UITabBarController{ enumD...
    明哥_Young閱讀 3,776評論 1 10
  • 常量與變量使用let來聲明常量隙疚,使用var來聲明變量。聲明的同時(shí)賦值的話磕道,編譯器會(huì)自動(dòng)推斷類型供屉。值永遠(yuǎn)不會(huì)被隱式轉(zhuǎn)...
    莫_名閱讀 436評論 0 1
  • 86.復(fù)合 Cases 共享相同代碼塊的多個(gè)switch 分支 分支可以合并, 寫在分支后用逗號分開。如果任何模式...
    無灃閱讀 1,347評論 1 5
  • 本章將會(huì)介紹 閉包表達(dá)式尾隨閉包值捕獲閉包是引用類型逃逸閉包自動(dòng)閉包枚舉語法使用Switch語句匹配枚舉值關(guān)聯(lián)值原...
    寒橋閱讀 1,552評論 0 3
  • 在夢里悼做。 我下班了,像往常一樣哗魂,徑直走去打卡機(jī)打卡肛走,不知為何,這次打了兩次卡录别。 忽然從背后傳來一個(gè)聲音:“你不能打...
    隔壁的隔壁老Z閱讀 264評論 0 1