swift學習筆記1——基礎部分

以前學習swift時的我的筆記,根據github:the-swift-programming-language-in-chinese學習、總結,將重要的內容提取,加以理解後整理爲學習筆記,方便之後查詢用。詳細能夠參考the-swift-programming-language-in-chinese,或者蘋果官方英文版文檔html

當前版本是swift2.2ios

print函數

函數原型
print(items, separator: String, terminator: String)git

參數:github

  • items:要打印的變量,或常量
  • separator:多個item參數之間的間隔符
  • terminator:打印的末尾能夠增長一個字符串

weak與unowned

在OC中,當__weak指向的對象被釋放時,weak指向nil,在swift中也同樣,那麼weak修飾的變量應該爲可選類型。
unowned至關於oc中的_unsafe_unretained,對象釋放後後指向一塊固定的內存。swift

  • weak用在:當self對象優先於block調用時
  • unowned:當self對象在block調用時不會被釋放

swift中提供了捕獲列表更優雅的解決了這個問題api

註釋

支持 // 註釋和塊註釋/**/,還支持註釋嵌套(C語言不支持):數組

/* 這是第一個多行註釋的開頭
    /* 這是第二個被嵌套的多行註釋 */
這是第一個多行註釋的結尾 */

類型轉換

一百萬能夠兩種表示:1000000或1_000_000數據結構

let value = 3 + 0.14159 // value最終類型爲Double類型,原始值3沒有顯式聲明類型,0.14159默認爲Double類型

而下面的例子,則不能夠:app

let three = 3
let pointOneFourOneFiveNine = 0.14159
let pi = three + pointOneFourOneFiveNine // 錯誤

let integerPi = Int(pi)
// integerPi 等於 3,因此被推測爲 Int 類型。浮點值會被截斷。也就是說4.75會變成4,-3.9會變成-3。

不一樣類型變量不支持隱式轉換.在下面的例子中,常量twoThousand是UInt16類型,然而常量one是UInt8類型。它們不能直接相加,由於它們類型不一樣。因此要調用UInt16(one)來建立一個新的UInt16數字並用one的值來初始化,而後使用這個新數字來計算:less

let twoThousand: UInt16 = 2_000
let one: UInt8 = 1
let twoThousandAndOne = twoThousand + UInt16(one)
如今兩個數字的類型都是UInt16,能夠進行相加。目標常量twoThousandAndOne的類型被推斷爲UInt16,由於它是兩個UInt16值的和。

UInt16(one)是調用 Swift 構造器並傳入一個初始值的默認方法。在語言內部,UInt16有一個構造器,能夠接受一個UInt8類型的值,因此這個構造器能夠用現有的UInt8來建立一個新的UInt16。注意,你並不能傳入任意類型的值,只能傳入UInt16內部有對應構造器的值。不過你能夠擴展示有的類型來讓它能夠接收其餘類型的值

float和double類型也不能直接相加

類型別名

typealias AudioSample = UInt16

定義了一個類型別名以後,你能夠在任何使用原始名的地方使用別名:

var maxAmplitudeFound = AudioSample.min // 值爲0

元組

把多個值組合成一個複合值。元組內的值能夠是任意類型,並不要求是相同類型。

你能夠將一個元組的內容分解(decompose)成單獨的常量和變量,而後你就能夠正常使用它們了:

let http404Error = (404, "Not Found")  
let (statusCode, statusMessage) = http404Error
print("The status code is \(statusCode)") // 輸出 "The status code is 404"
print("The status message is \(statusMessage)") // 輸出 "The status message is Not Found"

若是你只須要一部分元組值,分解的時候能夠把要忽略的部分用下劃線(_)標記:

let (justTheStatusCode, _) = http404Error
print("The status code is \(justTheStatusCode)") // 輸出 "The status code is 404"

此外,你還能夠經過下標來訪問元組中的單個元素,下標從零開始:

print("The status code is \(http404Error.0)") // 輸出 "The status code is 404"
print("The status message is \(http404Error.1)") // 輸出 "The status message is Not Found"

你能夠在定義元組的時候給單個元素命名:

let http200Status = (statusCode: 200, description: "OK")

給元組中的元素命名後,你能夠經過名字來獲取這些元素的值:

print("The status code is \(http200Status.statusCode)") // 輸出 "The status code is 200"
print("The status message is \(http200Status.description)") // 輸出 "The status message is OK"

元組在臨時組織值的時候頗有用,可是並不適合建立複雜的數據結構。若是你的數據結構並非臨時使用,請使用類或者結構體而不是元組。請參考類和結構體。

可選類型

可選類型不能像普通類型同樣使用,好比兩個可選類型相加會報錯,不僅相加,加減乘除等等
若是你聲明一個可選常量或者變量可是沒有賦值,它們會自動被設置爲nil:

var surveyAnswer: String? // surveyAnswer 被自動設置爲 nil

一個變量或常量被定義爲可選類型,那麼它就不是原來的類型了,而是可選類型,只有解析後爲有值才能夠回到原來的類型。

let inta:Int? = 3
let intb:Int! = 4
print(inta + intb)  // 錯誤,正確應該是inta! + intb
if inta != nil {
    print(inta + intb)  // 錯誤,正確應該是inta! + intb
}

if let intc = inta {    
    print(intc + intb)  // 可選綁定能夠當非可選類型使用
}

隱式解析可選類型

一個可選類型總會有值,這種類型的可選狀態被定義爲隱式解析可選類型
好比例子中的intb被定義爲"隱式解析可選類型",它仍是可選類型,能夠賦值爲nil,也能夠可選綁定。
在使用隱式解析可選類型時,不用加!,系統會自動解析,就從可選類型變爲它自己的類型,能夠把隱式解析可選類型當作一個能夠自動解析的可選類型

var intb:Int! = 4 // 隱式解析可選類型
intb = nil  // 賦值爲nil
if let intc = intb { // 可選綁定
    print(intc + intb)
}

強制解析

當你肯定可選類型確實包含值以後,你能夠在可選的名字後面加一個感嘆號(!)來獲取值,這被稱爲可選值的強制解析。強制解析必定要確保有值,不然會程序崩潰。

nil

在oc中表明一個空指針(指向不存在對象的指針)。僅僅限於對象
在swift中表明一個可選值的狀態爲沒有值,它是一個肯定的值,用來表示值缺失,不只限於對象

可選綁定

使用可選綁定(optional binding)來判斷可選類型是否包含值,若是包含就把值賦給一個臨時常量或者變量。可選綁定能夠用在if和while語句中,這條語句不只能夠用來判斷可選類型中是否有值,同時能夠將可選類型中的值賦給一個常量或者變量,賦給的常量或者變量的做用域只是在if緊跟的大括號內,賦值失敗,賦值成功、失敗做爲if判斷的Bool條件

let string : String? = nil
if let constantName = string { // 這裏的string必須爲可選類型,否則會報錯
    print(constantName)
} else {
    print(constantName)  // 錯誤: 超出constantName做用域
}

使用斷言進行調試

斷言會在運行時判斷一個邏輯條件是否爲true

你可使用全局assert(_:_file:line:)函數來寫一個斷言。向這個函數傳入一個結果爲true或者false的表達式以及一條信息,當表達式的結果爲false的時候這條信息會被顯示:

let age = -3
assert(age >= 0, "A person's age cannot be less than zero")
// 由於 age < 0,因此斷言會觸發

在這個例子中,只有age >= 0爲true的時候,即age的值非負的時候,代碼纔會繼續執行。若是age的值是負數,就像代碼中那樣,age >= 0爲false,斷言被觸發,終止應用。

使用場景:

  • 整數類型的下標索引被傳入一個自定義下標實現,可是下標索引值可能過小或者太大。
  • 須要給函數傳入一個值,可是非法的值可能致使函數不能正常執行。
  • 一個可選值如今是nil,可是後面的代碼運行須要一個非nil值。

運算符

Swift 支持大部分標準 C 語言的運算符,且改進許多特性來減小常規編碼錯誤。如:賦值符(=)不返回值,以防止把想要判斷相等運算符(==)的地方寫成賦值符致使的錯誤

算術運算符(+,-,*,/,%等)會檢測並不容許值溢出,以此來避免保存變量時因爲變量大於或小於其類型所能承載的範圍時致使的異常結果

區別於 C 語言,在 Swift 中你能夠對浮點數進行取餘運算(%),Swift 還提供了 C 語言沒有的表達兩數之間的值的區間運算符(a..<b 和 a...b),這方便咱們表達一個區間內的數值。

8 % 2.5   // 等於 0.5  3個2.5  還剩餘一個0.5

當元組中的值能夠比較時,你也可使用這些運算符來比較它們的大小。例如,由於 Int 和 String 類型的值能夠比較,因此類型爲 (Int, String) 的元組也能夠被比較。相反,Bool 不能被比較,也意味着存有布爾類型的元組不能被比較。

比較元組大小會按照從左到右、逐值比較的方式,直到發現有兩個值不等時中止。若是全部的值都相等,那麼這一對元組咱們就稱它們是相等的。例如:

(1, "zebra") < (2, "apple")   // true,由於 1 小於 2
(3, "apple") < (3, "bird")    // true,由於 3 等於 3,可是 apple 小於 bird
(4, "dog") == (4, "dog")      // true,由於 4 等於 4,dog 等於 dog

注意: Swift 標準庫只能比較七個之內元素的元組比較函數。若是你的元組元素超過七個時,你須要本身實現比較運算符。

空合運算符

空合運算符(a ?? b)將對可選類型 a 進行空判斷,若是 a 包含一個值就進行解封,不然就返回一個默認值 b。表達式 a 必須是 Optional 類型。默認值 b 的類型必需要和 a 存儲值的類型保持一致。

注意: 若是 a 爲非空值(non-nil),那麼值 b 將不會被計算。這也就是所謂的短路求值,短路運算符通常有:&&、||,其原理是:當有多個表達式時,左邊的表達式值能夠肯定結果時,就再也不繼續運算右邊的表達式的值

閉區間運算符

閉區間運算符(a...b)定義一個包含從 a 到 b(包括 a 和 b)的全部值的區間。a 的值不能超過 b

半開區間運算符

半開區間(a..<b)定義一個從 a 到 b 但不包括 b 的區間

邏輯運算符

邏輯與 或 非運算符的操做數只能是bool類型,不能是整型等等。Swift 邏輯操做符 && 和 || 是左結合的

字符串

定義空字符串

var emptyString = ""               // 空字符串字面量
var anotherEmptyString = String()  // 初始化方法
// 兩個字符串均爲空並等價。

您能夠經過檢查其Bool類型的isEmpty屬性來判斷該字符串是否爲空:

if emptyString.isEmpty {
    print("Nothing to see here")
}

字符串是值類型(Strings Are Value Types)

Swift 的String類型是值類型。 若是您建立了一個新的字符串,那麼當其進行常量、變量賦值操做,或在函數/方法中傳遞時,會進行值拷貝。 任何狀況下,都會對已有字符串值建立新副本,並對該新副本進行傳遞或賦值操做

在實際編譯時,Swift 編譯器會優化字符串的使用,使實際的複製只發生在絕對必要的狀況下,這意味着您將字符串做爲值類型的同時能夠得到極高的性能。

另外,經過標明一個Character類型並用字符字面量進行賦值,能夠創建一個獨立的字符常量或變量:

let exclamationMark: Character = "!"  // 不能用'',必須雙引號,並且必須指定爲Character類型,不然會推斷爲string類型

注意: 可擴展的字符羣集能夠組成一個或者多個 Unicode 標量。這意味着不一樣的字符以及相同字符的不一樣表示方式可能須要不一樣數量的內存空間來存儲。因此 Swift 中的字符在一個字符串中並不必定佔用相同的內存空間數量。所以在沒有獲取字符串的可擴展的字符羣的範圍時候,就不能計算出字符串的字符數量。若是您正在處理一個長字符串,須要注意characters屬性必須遍歷所有的 Unicode 標量,來肯定字符串的字符數量。
另外須要注意的是經過characters屬性返回的字符數量並不老是與包含相同字符的NSString的length屬性相同。NSString的length屬性是利用 UTF-16 表示的十六位代碼單元數字,而不是 Unicode 可擴展的字符羣集。做爲佐證,當一個NSString的length屬性被一個Swift的String值訪問時,其實是調用了utf16Count。

字符串索引

每個String值都有一個關聯的索引(index)類型,String.Index,它對應着字符串中的每個Character的位置。

前面提到,不一樣的字符可能會佔用不一樣數量的內存空間,因此要知道Character的肯定位置,就必須從String開頭遍歷每個 Unicode 標量直到結尾。所以,Swift 的字符串不能用整數(integer)作索引。

使用startIndex屬性能夠獲取一個String的第一個Character的索引。使用endIndex屬性能夠獲取最後一個Character的後一個位置的索引。所以,endIndex屬性不能做爲一個字符串的有效下標。若是String是空串,startIndex和endIndex是相等的。

let greeting = "Guten Tag!"
greeting[greeting.startIndex] // G
greeting[greeting.endIndex.predecessor()]  // !
greeting[greeting.startIndex.successor()]  // u
let index = greeting.startIndex.advancedBy(7) // 這個比較經常使用,()裏面能夠是負值
greeting[index]   // a

若是字符串索引越界,則運行時錯誤

字符串/字符相等

字符串/字符能夠用等於操做符(==)和不等於操做符(!=)

若是兩個字符串(或者兩個字符)的可擴展的字形羣集是標準相等的,那就認爲它們是相等的。在這個狀況下,即便可擴展的字形羣集是有不一樣的 Unicode 標量構成的,只要它們有一樣的語言意義和外觀,就認爲它們標準相等。

相反,英語中的LATIN CAPITAL LETTER A(U+0041,或者A)不等於俄語中的CYRILLIC CAPITAL LETTER A(U+0410,或者A)。兩個字符看着是同樣的,但卻有不一樣的語言意義

前綴/後綴相等 (Prefix and Suffix Equality)

經過調用字符串的hasPrefix(_:)/hasSuffix(_:)方法來檢查字符串是否擁有特定前綴/後綴,兩個方法均接收一個String類型的參數,並返回一個布爾值.

let astring = "123456"
astring.hasPrefix("123") // true
astring.hasSuffix("456") // true

Arrays、Sets和Dictionaries三種集合

Swift 語言提供Arrays、Sets和Dictionaries三種基本的集合類型用來存儲集合數據。數組(Arrays)是有序數據的集。集合(Sets)是無序無重複數據的集。字典(Dictionaries)是無序的鍵值對的集

Swift 語言中的Arrays、Sets和Dictionaries中存儲的數據值類型必須明確。這意味着咱們不能把不正確的數據類型插入其中。同時這也說明咱們徹底能夠對取回值的類型很是自信

對於無序集合 可使用sort()來進行排序

數組(Arrays)

數組使用有序列表存儲同一類型的多個值,可選類型與通常類型不能同一存儲(隱式解析可選類型能夠當作通常類型使用)。相同的值能夠屢次出如今一個數組的不一樣位置中。若是初始化時,全部內容類型一致,擇數組中保存的是該類型的內容
若是初始化時,全部內容類型不一致,則數組中保存的是 NSObject,類型一旦肯定則不能改變

var someInts = [Int]() // 定義
someInts = []   // someInts 如今是空數組,可是仍然是 [Int] 類型的。

若是數組定義爲let,則數組的內容不能改變,數組自己也不能變。若是是var則二者均可變,另外

建立一個帶有默認值的數組

Swift 中的Array類型還提供一個能夠建立特定大小而且全部數據都被默認的構造方法

var threeDoubles = [Double](count: 3, repeatedValue:0.0)
// threeDoubles 是一種 [Double] 數組,等價於 [0.0, 0.0, 0.0]

經過兩個數組相加建立一個數組

咱們可使用加法操做符(+)來組合兩種已存在的相同類型數組。新數組的數據類型會被從兩個數組的數據類型中推斷出來:

var anotherThreeDoubles = Array(count: 3, repeatedValue: 2.5)
// anotherThreeDoubles 被推斷爲 [Double],等價於 [2.5, 2.5, 2.5]

var sixDoubles = threeDoubles + anotherThreeDoubles
// sixDoubles 被推斷爲 [Double],等價於 [0.0, 0.0, 0.0, 2.5, 2.5, 2.5]

使用加法賦值運算符(+=)也能夠直接在數組後面添加一個或多個擁有相同類型的數據項:

shoppingList += ["Baking Powder"]
// shoppingList 如今有四項了
shoppingList += ["Chocolate Spread", "Cheese", "Butter"]
// shoppingList 如今有七項了

還能夠利用下標來一次改變一系列數據值,即便新數據和原有數據的數量是不同的。下面的例子把"Chocolate Spread","Cheese",和"Butter"替換爲"Bananas"和 "Apples":

shoppingList[4...6] = ["Bananas", "Apples"]

若是咱們只想把數組中的最後一項移除,可使用removeLast()方法

不能夠用下標訪問的形式去在數組尾部添加新項。

若是咱們同時須要每一個數據項的值和索引值,可使用enumerate()方法來進行數組遍歷。enumerate()返回一個由每個數據項索引值和數據值組成的元組。咱們能夠把這個元組分解成臨時常量或者變量來進行遍歷:

for (index, value) in shoppingList.enumerate() {
    print("Item \(String(index + 1)): \(value)")
}

集合(Sets)

集合(Set)用來存儲相同類型而且沒有肯定順序的值。當集合元素順序不重要時或者但願確保每一個元素只出現一次時可使用集合而不是數組。

集合類型的哈希值

一個類型爲了存儲在集合中,該類型必須是可哈希化的--也就是說,該類型必須提供一個方法來計算它的哈希值。一個哈希值是Int類型的,相等的對象哈希值必須相同,好比a==b,所以必須a.hashValue == b.hashValue。

集合類型的哈希值

一個類型爲了存儲在集合中,該類型必須是可哈希化的--也就是說,該類型必須提供一個方法來計算它的哈希值。一個哈希值是Int類型的,相等的對象哈希值必須相同,好比a==b,所以必須a.hashValue == b.hashValue。

var letters = Set<Character>()  // 建立和構造一個空的集合
letters = []  // letters 如今是一個空的 Set, 可是它依然是 Set<Character> 類型
var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"]

通常操做

一個Set類型不能從數組字面量中被單獨推斷出來,所以Set類型必須顯式聲明。然而,因爲 Swift 的類型推斷功能,若是你想使用一個數組字面量構造一個Set而且該數組字面量中的全部元素類型相同,那麼你無須寫出Set的具體類型

var favoriteGenres: Set = ["Rock", "Classical", "Hip hop"]

你能夠經過調用Set的insert(_:)方法來添加一個新元素,你能夠經過調用Set的remove(_:)方法去刪除一個元素,若是該值是該Set的一個元素則刪除該元素而且返回被刪除的元素值,不然若是該Set不包含該值,則返回nil。另外,Set中的全部元素能夠經過它的removeAll()方法刪除。使用contains(_:)方法去檢查Set中是否包含一個特定的值,你能夠在一個for-in循環中遍歷一個Set中的全部值。

集合比較

  • 使用intersect(_:)方法根據兩個集合中都包含的值建立的一個新的集合。
  • 使用exclusiveOr(_:)方法根據在一個集合中但不在兩個集合中的值建立一個新的集合。
  • 使用union(_:)方法根據兩個集合的值建立一個新的集合。
  • 使用subtract(_:)方法根據不在該集合中的值建立一個新的集合。

字典

字典是一種存儲多個相同類型的值的容器。每一個值(value)都關聯惟一的鍵(key),鍵做爲字典中的這個值數據的標識符。和數組中的數據項不一樣,字典中的數據項並無具體順序

字典類型快捷語法

Swift 的字典使用Dictionary<Key, Value>定義,咱們也能夠用[Key: Value]這樣快捷的形式去建立一個字典類型。雖然這兩種形式功能上相同,可是後者是首選,而且這本指導書涉及到字典類型時通篇採用後者。

和數組同樣,咱們在用字典字面量構造字典時,若是它的鍵和值都有各自一致的類型,那麼就沒必要寫出字典的類型。

建立一個空字典

咱們能夠像數組同樣使用構造語法建立一個擁有肯定類型的空字典:

var namesOfIntegers = [Int: String]()  // namesOfIntegers 是一個空的 [Int: String] 字典

這個例子建立了一個[Int: String]類型的空字典來儲存整數的英語命名。它的鍵是Int型,值是String型。

若是上下文已經提供了類型信息,咱們可使用空字典字面量來建立一個空字典,記做[:](中括號中放一個冒號):

namesOfIntegers[16] = "sixteen"
// namesOfIntegers 如今包含一個鍵值對
namesOfIntegers = [:]
// namesOfIntegers 又成爲了一個 [Int: String] 類型的空字典

用字典字面量建立字典
咱們可使用字典字面量來構造字典,這和咱們剛纔介紹過的數組字面量擁有類似語法。字典字面量是一種將一個或多個鍵值對寫做Dictionary集合的快捷途徑。

一個鍵值對是一個key和一個value的結合體。在字典字面量中,每個鍵值對的鍵和值都由冒號分割。這些鍵值對構成一個列表,其中這些鍵值對由方括號包含、由逗號分割:

[key 1: value 1, key 2: value 2, key 3: value 3]

iocoresurfaceroot

控制流

Swift 的switch語句比 C 語言中更增強大。在 C 語言中,若是某個 case 不當心漏寫了break,這個 case 就會貫穿至下一個 case,Swift 無需寫break,因此不會發生這種貫穿的狀況,但你仍然能夠在末尾寫入break,來正常或提早結束。case 還能夠匹配更多的類型模式,包括區間匹配(range matching),元組(tuple)和特定類型的描述。switch的 case 語句中匹配的值能夠是由 case 體內部臨時的常量或者變量決定,也能夠由where分句描述更復雜的匹配條件。

For-In 循環

若是你不須要知道區間序列內每一項的值,你可使用下劃線(_)替代變量名來忽略對值的訪問:

let base = 3
let power = 10
var answer = 1
for _ in 1...power {
    answer *= base
}

repeat-while循環合其餘語言中的do-while循環是相似的

switch語句必須是完備的,即必須有default分支,除非是字面常量,不然報錯,以下:

switch 10 {
case 10:
    print("10")
}
/// 或者是窮舉枚舉類型也能夠不用default

另外,能夠有多個匹配,若是有多個匹配則只執行第一個,break語句是跳出當前循環,或者是跳出switch分支,所以在其它地方使用swift中會報錯,而continue只容許在循環裏面使用。每一個分支必須至少有一條執行語句,包括default分支

若是你確實須要 C 風格的貫穿的特性,你能夠在每一個須要該特性的 case 分支中使用fallthrough關鍵字,將自動執行下一個分支.

標籤語句

在 Swift 中,你能夠在循環體和switch代碼塊中嵌套循環體和switch代碼塊來創造複雜的控制流結構。然而,循環體和switch代碼塊二者均可以使用break語句來提早結束整個方法體。所以,顯式地指明break語句想要終止的是哪一個循環體或者switch代碼塊,會頗有用。相似地,若是你有許多嵌套的循環體,顯式指明continue語句想要影響哪個循環體也會很是有用。

labelName: while condition { statements }

gameLoop: while square != finalSquare {
    switch square + diceRoll {
    case finalSquare:
        break gameLoop // 跳出while循環
    case let newSquare where newSquare > finalSquare:
        continue gameLoop // 開始新的while循環
    }
}

檢測 API 可用性

if #available(iOS 9, OSX 10.10, *) {
    // 在 iOS 使用 iOS 9 的 API, 在 OS X 使用 OS X v10.10 的 API
} else {
    // 使用先前版本的 iOS 和 OS X 的 API
}

以上可用性條件指定在 iOS,if段的代碼僅僅在 iOS 9 及更高可運行;在 OS X,僅在 OS X v10.10 及更高可運行。最後一個參數,*,是必須的而且指定在任何其餘平臺上,if段的代碼在最小可用部署目標指定項目中執行。

相關文章
相關標籤/搜索