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ù)值類型
學(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,存儲(chǔ)在棧區(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)映琳。
屬性按類別可以分為存儲(chǔ)屬性和計(jì)算屬性机隙,按照可變性可分為變量屬性和常量屬性,根據(jù)歸屬權(quán)可以分為實(shí)例屬性和類型屬性(靜態(tài)屬性)
為了更好的的理解屬性的本質(zhì)萨西,我們要學(xué)會(huì)查看sil中間格式有鹿,在命令行輸入
xcrun swift -emit-silgen point.swift -o point.sil
這樣就生成了一個(gè)point.sil文件,可以通過文件編輯器查看其中的代碼谎脯。
存儲(chǔ)屬性的本質(zhì)構(gòu)成是一個(gè)存儲(chǔ)變量和兩個(gè)訪問器方法(ge方法和set方法)
計(jì)算屬性沒有一個(gè)存儲(chǔ)的變量葱跋,但是他也有兩個(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)
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)
存儲(chǔ)屬性的生存周期
實(shí)例存儲(chǔ)屬性的生命周期跟隨實(shí)例本身娱俺,實(shí)例銷毀了實(shí)例存儲(chǔ)屬性也一同銷毀。
靜態(tài)存儲(chǔ)屬性的值存儲(chǔ)在靜態(tài)數(shù)據(jù)區(qū)废麻,生命周期從類型加載開始荠卷,到類型卸載結(jié)束(生存周期非常長)。
值存儲(chǔ)屬性直接“內(nèi)嵌”在實(shí)例中烛愧。
引用存儲(chǔ)屬性通過指針“強(qiáng)引用”堆上的引用類型實(shí)例油宜,ARC系統(tǒng)會(huì)對引用數(shù)進(jìn)行管理掂碱,當(dāng)引用數(shù)為0時(shí)銷毀。
Lazy存儲(chǔ)屬性:如果有的類型對象占用內(nèi)存空間很大验庙,可以使用Lazy屬性延遲初值計(jì)算到訪問時(shí)顶吮。
class DataProcessor{
lazy var myData=LargeData()
}
屬性初始化:類中所有的屬性必須初始化社牲,要么提供默認(rèn)值 或者 構(gòu)造器初始化粪薛。結(jié)構(gòu)中的存儲(chǔ)屬性可以不用程序員寫初始化器,編譯器會(huì)自動(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í)初始化器
初始化器用于初始化類型實(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)值飞苇,也可以自定義初始值菌瘫。所有存儲(chǔ)屬性必須被初始化,實(shí)例存儲(chǔ)屬性可以使用初始化器初始化布卡,類型存儲(chǔ)屬性不可以使用初始化器雨让,所以必須指定默認(rèn)值。
可選屬性類型可以不初始化
//可選屬性類型初始化時(shí)要在后面加上一個(gè)問號(hào)‘忿等?’栖忠,實(shí)際上是編譯器把數(shù)值初始化成了nil
class Product{
var no:Int?
var description: String?
}
存儲(chǔ)屬性被初始化時(shí),不會(huì)調(diào)用屬性觀察者贸街。
默認(rèn)初始化器
一個(gè)類可以有多個(gè)初始化器庵寞,但至少需要一個(gè)初始化器。如果一個(gè)類沒有提供初始化器薛匪,那么編譯器會(huì)自動(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)識(shí)析構(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會(huì)立即調(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ù)
下標(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í)例值是否相等薯演,自定義類型需要重載提供“值比較語義”。相等操作符==秧了,不相等操作符!=