GeekBand Swift高級編程(第一周)

Swift語言簡介

Swift是在Objective-C語言的基礎(chǔ)上發(fā)展而來的一門現(xiàn)代高級語言。由蘋果公司于2014年6月推出,目前已經(jīng)是2.0版本。
Swift與Cocoa和Cocoa Touch框架高度集成,支持開發(fā)Mac OS X、iOS、watchOS應(yīng)用。基于Objective-C運(yùn)行時(shí),支持與Objective-C語言雙向互操作。

Swift語言有三種開發(fā)方式
Playground:交互式編寫代碼,適合快速學(xué)習(xí)、測試、可視化觀察
REPL(Read-Eval-Print-Loop)命名行:生成.swift文件之后在在命名行下鍵入 xcrun swiftc 文件名.swift 來編譯運(yùn)行。適合調(diào)試、研究、微觀探查。
Xcode項(xiàng)目:構(gòu)建正規(guī)工程項(xiàng)目,使用大型框架,追求設(shè)計(jì)質(zhì)量與代碼組織。

Swift的編譯過程
相較于Objective-C的編譯過程,在前端和LLVM IR優(yōu)化之間多了一步叫SIL Optimizer(Swift中間語言優(yōu)化)的步驟。

編譯過程

類型系統(tǒng)
值類型 value type有:
基礎(chǔ)數(shù)值類型
結(jié)構(gòu) struct
枚舉 enum
元組 tuple

特殊值類型有:
字符串 string
數(shù)組 Array
字典 Dictionary
集 Set

引用類型 reference type
類 class
閉包 closure

類型裝飾
協(xié)議 protocol
擴(kuò)展 extension
泛型 generics

基礎(chǔ)數(shù)值類型

基礎(chǔ)數(shù)值類型

學(xué)習(xí)資料
**Swift官方資源 **https://developer.apple.com/swift/resources/

值類型與引用類型

類型成員Type Member
屬性 property:數(shù)據(jù)成員 data member,描述對象狀態(tài)
方法 method:函數(shù)成員 function member,描述對象行為
初始化器 init
析構(gòu)器 deinit
下標(biāo) subscript

class MyClass {
    
    //屬性
    var x:Int
    var y:Int
    var datas:[Int]=[1,2,3,4,5]
    
    //初始化器
    init(x:Int, y:Int){
        self.x=x
        self.y=y
    }
    
    //方法
    func print(){
       println("\(x), \(y), \(datas)")
    }
    
    
    //下標(biāo)
    subscript(index: Int) -> Int {
        get {
            return datas[index]
        }
        set(newValue) {
            datas[index]=newValue
        }
    }
    
    //析構(gòu)器
    deinit{
        println("clear up resources")
    }
}

類與結(jié)構(gòu)class and struct
類的實(shí)例叫對象(object),是一個(gè)引用類型,在棧上就是一個(gè)指針,指向一個(gè)位于堆上的實(shí)體對象。

引用類型的空間分析

結(jié)構(gòu)的實(shí)例叫值(value),是一個(gè)值類型,實(shí)例直接位于棧中。
值類型的空間分析

對象和值再不同的內(nèi)存空間(棧和堆)拷貝和傳參過程分析


class RPoint{
    var x:Int
    var y:Int
    
    init(x:Int, y:Int){
        self.x=x
        self.y=y
    }
}

struct SPoint{
    var x:Int
    var y:Int
    
    init(x:Int, y:Int){
        self.x=x
        self.y=y
    }
}

var rp=RPoint(x:10,y:20)
var sp=SPoint(x:10,y:20)

func foo(){
    
    var rp1=RPoint(x:10,y:20)
    var rp2=rp1
    var sp1=SPoint(x:10,y:20)
    var sp2=sp1
    
    rp1.x++
    rp1.y++

    sp1.x++
    sp1.y++ 
}


func foo1(){
    var rp1=RPoint(x:10,y:20)
    var sp1=SPoint(x:10,y:20)
    
    foo2(rp1,sp1)
}

func foo2(var rp2: RPoint, var sp2: SPoint){
    rp2.x++
    rp2.y++
    
    sp2.x++
    sp2.y++
}

foo()
foo1()

根據(jù)以上代碼運(yùn)行后,foo函數(shù)的內(nèi)存模型如下,sp1是value,經(jīng)過復(fù)制sp2操作后,sp1和sp2都是value,存儲在棧區(qū)域內(nèi)。
rp1是object,復(fù)制rp2操作后,只是在棧區(qū)域復(fù)制了一個(gè)地址rp2,指向的還是和rp1同一塊堆空間。


拷貝行為

根據(jù)以上代碼運(yùn)行后,foo1和foo2函數(shù)的內(nèi)存模型如下,當(dāng)一個(gè)value(sp1)做為參數(shù)傳遞時(shí),接收的函數(shù)將value的值再復(fù)制一份到自己的棧中,當(dāng)object(sp1)做為參數(shù)傳遞時(shí),接收的函數(shù)只復(fù)制一份相同的地址存到自己的棧中,并且指向同一塊堆空間。


傳參行為

類型成員:屬性

屬性用來表達(dá)實(shí)例狀態(tài)或類型狀態(tài)。
屬性按類別可以分為存儲屬性和計(jì)算屬性,按照可變性可分為變量屬性和常量屬性,根據(jù)歸屬權(quán)可以分為實(shí)例屬性和類型屬性(靜態(tài)屬性)

為了更好的的理解屬性的本質(zhì),我們要學(xué)會查看sil中間格式,在命令行輸入
xcrun swift -emit-silgen point.swift -o point.sil
這樣就生成了一個(gè)point.sil文件,可以通過文件編輯器查看其中的代碼。

存儲屬性的本質(zhì)構(gòu)成是一個(gè)存儲變量和兩個(gè)訪問器方法(ge方法和set方法)
計(jì)算屬性沒有一個(gè)存儲的變量,但是他也有兩個(gè)訪問器方法,也可以簡化為一個(gè)get方法。

struct SPoint{
    var x:Int
    var y:Int
}

class RPoint{
    var x:Int
    var y:Int
    static var max=100 //靜態(tài)變量屬性
    
    init(x:Int,y:Int){
        self.x=x
        self.y=y
    }
}

var sp=SPoint(x:10,y:20)
var rp=RPoint(x:10,y:20)
值存儲屬性的內(nèi)存模型
struct SCircle{
    var center:RPoint
    var radius:Int
}

class RCircle{
    var center:RPoint
    var radius:Int
    
    init(center:RPoint, radius:Int){
        self.center=center
        self.radius=radius
    }
    
    static var min=RPoint(x:0,y:0)
}

var sc=SCircle(center:rp, radius:10)
var rc=RCircle(center:rp, radius:100)
引用存儲屬性的內(nèi)存模型.jpg

存儲屬性的生存周期
實(shí)例存儲屬性的生命周期跟隨實(shí)例本身,實(shí)例銷毀了實(shí)例存儲屬性也一同銷毀。
靜態(tài)存儲屬性的值存儲在靜態(tài)數(shù)據(jù)區(qū),生命周期從類型加載開始,到類型卸載結(jié)束(生存周期非常長)。

值存儲屬性直接“內(nèi)嵌”在實(shí)例中。
引用存儲屬性通過指針“強(qiáng)引用”堆上的引用類型實(shí)例,ARC系統(tǒng)會對引用數(shù)進(jìn)行管理,當(dāng)引用數(shù)為0時(shí)銷毀。

Lazy存儲屬性:如果有的類型對象占用內(nèi)存空間很大,可以使用Lazy屬性延遲初值計(jì)算到訪問時(shí)。

class DataProcessor{
    lazy var myData=LargeData()
}

屬性初始化:類中所有的屬性必須初始化,要么提供默認(rèn)值 或者 構(gòu)造器初始化。結(jié)構(gòu)中的存儲屬性可以不用程序員寫初始化器,編譯器會自動(dòng)幫我們添加構(gòu)造方法。
屬性觀察者:檢測改變
willSet 在改變前調(diào)用
didSet 在改變后調(diào)用

class DataStorage {
    
    var data: Int = 0 {
        willSet {
            print("About to set data to \(newValue)")
        }
        didSet {
            print("Changed from \(oldValue) to \(data)")
        }
    }
}

類型成員:方法

函數(shù):代碼段上的可執(zhí)行指令序列
全局函數(shù)

func add(data1:Int, data2:Int)-> Int{
    return data1+data2
}

成員函數(shù)
成員函數(shù)也叫方法,方法是類型的成員函數(shù),表達(dá)行為,方法可以定義與以下類型:class,struct,enum

class MyClass {
    var instanceData=100
    static var typeData=10000
 
    //實(shí)例方法
    func instanceMethod(){
        ++instanceData
        ++MyClass.typeData  
        instanceMethod2()
        MyClass.typeMethod()
    }
    
    //靜態(tài)方法
    static func typeMethod(){
        ++typeData
        //++instanceData
        typeMethod2()
        //instanceMethod()
    }
}

歸屬權(quán)
實(shí)例方法-反應(yīng)的是一個(gè)實(shí)例行為,可以訪問實(shí)例方法與屬性、以及類型屬性與方法。
類型方法-類型行為,只能訪問類型屬性與方法。

方法參數(shù)
func(參數(shù)1,參數(shù)2...)->返回值{ }
參數(shù)形式:外部參數(shù)名 本地參數(shù)名:類型
聲明時(shí)可以省略外部參數(shù)名,這時(shí)外部參數(shù)名使用本地參數(shù)名。
調(diào)用時(shí),第一個(gè)參數(shù)名可忽略,但后面的參數(shù)名必須顯示標(biāo)明。如果在聲明時(shí)加_,調(diào)用時(shí)也可以忽略參數(shù)名
方法可以沒有參數(shù),也可以沒有返回值
參數(shù)傳遞默認(rèn)為傳值方式

//顯式內(nèi)部參數(shù)名,顯式外部參數(shù)名
    func sayGreeting(person name:String,greeting words:String)->String{
        return words+"! "+name
    }

//調(diào)用顯式內(nèi)部參數(shù)名,顯式外部參數(shù)名函數(shù)
myObject.sayGreeting(person:"Jason", greeting:"You are welcome")

//顯式內(nèi)部參數(shù)名,省略外部參數(shù)名
    func sayGreeting(name:String, _ words:String)->String{
        return words+"! "+name
    }
//調(diào)用顯式內(nèi)部參數(shù)名,省略外部參數(shù)名函數(shù),注意如果上面的_ words沒有寫下劃線,就不能省略第二個(gè)內(nèi)部參數(shù)名words
myObject.sayGreeting("Jason","Welcome")

//無參數(shù),無返回值
func sayGreeting(){
        print("Hello!")
    }

更多參數(shù)與返回值功能
提供參數(shù)默認(rèn)值

//設(shè)置參數(shù)的默認(rèn)值為Jason
func sayGreeting(name:String="Jason")->String {
        return "Hello! "+name
    }

常量參數(shù) VS 變量參數(shù)

//data1是常量參數(shù) data2是變量參數(shù)
func changeParameter(data1:Int, var data2:Int){
        var dataTemp=data1
        data2++
        dataTemp++
    }

可變數(shù)目參數(shù):實(shí)質(zhì)上傳遞的是數(shù)組

//可變數(shù)目參數(shù)
    func averageNumber(numbers:Double...)->Double{
    
        var sum=0.0
        for item in numbers {
            sum+=item
        }
        return sum / Double(numbers.count)
    }

//可變參數(shù)個(gè)數(shù)函數(shù)的調(diào)用
myObject.averageNumber(10,90,30,80,50,100)

inout參數(shù),可以改變外部實(shí)參,注意與引用類型參數(shù)區(qū)別

//inout參數(shù)
    func swap(inout a:Int,inout b:Int){
        let tempA=a
        a=b
        b=tempA
    }

//調(diào)用inout參數(shù)的函數(shù)
myObject.swap(&data1,b: &data2)

多個(gè)返回值:Tuple類型

//返回Tuple類型
    func minMax(array: [Int]) -> (min: Int, max: Int){
        var currentMin = array[0]
        var currentMax = array[0]
        for value in array[1..<array.count] {
            if value < currentMin {
                currentMin = value
            } else if value > currentMax {
                currentMax = value
            }
        }
        return (currentMin, currentMax)
    }

//返回Tuple類型的函數(shù)調(diào)用
let range=myObject.minMax([3,-9,23,15,-45,7])
print("max=\(range.max), min=\(range.min)")

類型成員:初始化器

認(rèn)識初始化器
初始化器用于初始化類型實(shí)例,是一個(gè)特殊的函數(shù),無返回值。初始化器以init開頭

//直接賦初值的初始化器
init(){
        x=10
        y=10
    }
    
//自定義初值的初始化器
    init(x:Int, y:Int){
        self.x=x
        self.y=y
        
        print("init is invoked")
    }

var pt1=Point()
//調(diào)用初始化器,初始化器的第一個(gè)參數(shù)名也不能省略
var pt2=Point(x: 100,y:200)

類型實(shí)例的初始化過程
1.分配內(nèi)存
2.調(diào)用舒適化器初始化內(nèi)存

初始化器可以定義與以下類型:class, struct, enum
只有實(shí)例初始化器,沒有類型初始化器

初始化實(shí)例屬性
初始化器可以指定默認(rèn)值,也可以自定義初始值。所有存儲屬性必須被初始化,實(shí)例存儲屬性可以使用初始化器初始化,類型存儲屬性不可以使用初始化器,所以必須指定默認(rèn)值。
可選屬性類型可以不初始化

//可選屬性類型初始化時(shí)要在后面加上一個(gè)問號‘?’,實(shí)際上是編譯器把數(shù)值初始化成了nil
class Product{
    var no:Int?
    var description: String?
}

存儲屬性被初始化時(shí),不會調(diào)用屬性觀察者。

默認(rèn)初始化器
一個(gè)類可以有多個(gè)初始化器,但至少需要一個(gè)初始化器。如果一個(gè)類沒有提供初始化器,那么編譯器會自動(dòng)生成一個(gè)默認(rèn)的初始化器,默認(rèn)的初始化器是一個(gè)無參數(shù)的初始化器init()。

指定初始化器 VS 便捷初始化器
指定初始化器(Designated Initializer)為類的主初始化器,負(fù)責(zé)初始化所有屬性。必須調(diào)用其父類的主初始化器。
便捷舒適化器(Convenience Initializer)為類的輔助初始化器。必須調(diào)用同類的指定初始化器。

初始化器
class Point3D{
    var x:Int
    var y:Int
    var z:Int
    //指定初始化器,也叫主初始化器
    init(x:Int, y:Int, z:Int){
        self.x=x
        self.y=y
        self.z=z
        
        //other processing
        print("other task")
    }
    //便捷初始化器,要加convenience關(guān)鍵字在函數(shù)前,函數(shù)內(nèi)部就可以調(diào)用指定初始化器了
    convenience init(x:Int, y:Int){      
        self.init(x:x,y:y,z:0)
    }
    
    convenience init(){   
        self.init(x:0,y:0,z:0)
    }
}

類型成員:析構(gòu)器

認(rèn)識析構(gòu)器
析構(gòu)器(deinit)在實(shí)例內(nèi)存被釋放前調(diào)用,用于釋放實(shí)例使用的非內(nèi)存資源。 析構(gòu)器僅可以定義于class,且只能定義一個(gè)。struct和enum不能定義析構(gòu)器。
歸屬權(quán):只有實(shí)例析構(gòu)器,沒有類型析構(gòu)器
析構(gòu)器的調(diào)用是由運(yùn)行時(shí)根據(jù)ARC的釋放規(guī)則調(diào)用,程序員無法手工調(diào)用。

class FileStream{
    init(){
        print("open file...")
    }
    
    func process(){
        print("process file...")
    }
    //deinit是析構(gòu)器,由于析構(gòu)器是一個(gè)特殊的函數(shù),所以之前不需要加func,也不需要加參數(shù)
    deinit{
        print("close file...")
    }
}

var fs:FileStream?

fs=FileStream() //引用計(jì)數(shù):1

fs!.process()

fs = nil //引用計(jì)數(shù):0 ARC在這時(shí)將自動(dòng)調(diào)用析構(gòu)器

ARC簡介
ARC(Automatic Reference Counting)自動(dòng)引用計(jì)數(shù),用于管理堆上的對象實(shí)例所分配的動(dòng)態(tài)內(nèi)存。在Swift語言中必須使用ARC管理機(jī)制,不可以關(guān)閉ARC來手動(dòng)管理。ARC不負(fù)責(zé)管理?xiàng)I系膬?nèi)存。棧上的內(nèi)存有運(yùn)行時(shí)根據(jù)函數(shù)生存周期來自動(dòng)管理?xiàng)5膭?chuàng)建和銷毀。ARC通過追蹤“對象倍引用的計(jì)數(shù)”來確定對象是否還需要被訪問,如果對象的引用計(jì)數(shù)為0,ARC會立即調(diào)用析構(gòu)器,并隨后釋放對象內(nèi)存。

引用計(jì)數(shù)管理
對象引用計(jì)數(shù)增加1:
新創(chuàng)建對象并賦值給變量

var fs:FileStream?

fs=FileStream() //引用計(jì)數(shù):1

將對象引用拷貝給其他變量或常量
var fs2 = fs //引用計(jì)數(shù):+1(等于2)
將對象賦值給其他屬性(無論實(shí)例屬性、還是類型屬性)

class MyClass{
    var myFile:FileStream?
    
    init(myFile: FileStream?){
        self.myFile=myFile
    }
}

var mc: MyClass?
mc = MyClass(myFile:nil)
mc!.myFile=fs //引用計(jì)數(shù):3

將對象傳遞給函數(shù)參數(shù)(非inout參數(shù)),或者返回值

func invoke(file: FileStream?){
    file!.process()
}

//調(diào)用時(shí)引用計(jì)數(shù)+1 : 4
invoke(fs)
//函數(shù)結(jié)束引用計(jì)數(shù)-1 : 3

對象引用計(jì)數(shù)減1:
將變量賦值為nil
fs = nil
將屬性賦值為nil,或者屬性所在的對象被釋放(實(shí)例屬性)
mc!.meFile = nil 或者 mc = nil
傳值參數(shù)離開函數(shù)

圖示引用計(jì)數(shù)

下標(biāo)與操作符

下標(biāo)(Subscripts)
下標(biāo)支持使用索引的方式訪問“集合式”實(shí)例,例如vector[index]
下標(biāo)可以定義于class, struct, enum
下標(biāo)可以類比為“含參的計(jì)算屬性”,其本質(zhì)是一對代索引參數(shù)的訪問器方法(get/set)

訪問下標(biāo)
歸屬權(quán):只能定義實(shí)例下標(biāo),不能定義類型下標(biāo)(靜態(tài)下標(biāo))
可以定義讀寫下標(biāo)(get和set),或者只讀下標(biāo)(get)
下標(biāo)的索引參數(shù)可以是任意類型,也可以設(shè)計(jì)多個(gè)參數(shù)
一個(gè)類型可以提供多個(gè)下標(biāo)的重載版本(參數(shù)不同)


class Vector{
    
    var datas = [Int](count:100,repeatedValue:0)
    
    subscript(index:Int)-> Int{
        get{
            
            return datas[index]
        }
        set{
            
            datas[index]=newValue
        }
    }
}

var datas=Vector()
for i in 0..<10{
   datas[i]=i
}

for i in 0..<10{
    print(datas[i])
}

標(biāo)準(zhǔn)操作符
賦值操作符:= 不返回結(jié)果
算術(shù)操作符:+ - * / % 檢測溢出錯(cuò)誤
自增、自減操作符:++ --
復(fù)合賦值操作符:+= -=
比較操作符:> < >= <=
羅輯操作符:&& || !
位操作符:~ & | ^ << >>
三元操作符:a ? b : c
范圍操作符:a..<b(a到小于b) a...b(a到b)
值相等:== !=
引用相等:=== !==

重載操作符
除了標(biāo)準(zhǔn)操作符之外,還可以針對自定義類型重載標(biāo)準(zhǔn)操作符。重載操作符又稱“操作符函數(shù)”,其本質(zhì)是全局函數(shù)。
可以重載前綴(prefix)、中綴(infix)、后綴(postfix)操作符。前綴和后綴操作符需要加關(guān)鍵詞:prefix 或 postfix
也可以通過將參數(shù)設(shè)置為inout參數(shù),重載復(fù)合賦值操作符,例如+=, -=
除了重載Swift標(biāo)準(zhǔn)操作符,也可以自定義新的操作符,并重載。

//中綴操作符
func + (left: Complex, right: Complex) -> Complex {
    
    return Complex(real: left.real + right.real,
        imag: left.imag + right.imag)
}

//前綴操作符
prefix func - (data: Complex) -> Complex {
    return Complex(real: -data.real, imag: -data.imag)
}

//復(fù)合賦值操作符
func += (inout left: Complex, right: Complex) {
    left = left + right
}

//自定義操作符
prefix operator +++ {}

prefix func +++ (inout data: Complex) -> Complex {
    data += data
    return data
}

相等操作符
引用相等:判斷對象地址是否相等,僅針對引用類型,不適用于值類型。相等操作符===,不相等操作符!==
值相等:判斷實(shí)例值是否相等,自定義類型需要重載提供“值比較語義”。相等操作符==,不相等操作符!=

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容