# Swift 文法筆記 ###### tags: `Swift` # Swift ```篇章1``` ## Swift基本語法 ### 宣告 ```swift= //一般宣告 let 用來宣告常數 var 用來宣告變數 //因為是動態語言,所以資料型態會自己設定 let f = 0 //手動設定的話: let f:Float = 0 //設成Float型態 //甚至可以使用中文和表情符號作為變數及常數命名 let 香蕉 = "Banana" ## 型別轉換 ## //Swift屬於強型別(Strongly Typed)代表運算式中的型態要一致 let n = Int("100") //由字串轉整數 let s = String(20) //由數字轉字串 //字串轉二進位 let data = "Hello".data(using: .utf8) //二進位轉字串 let str = String(data:data!, encoding: .utf8) ## as ##(型態中的轉換) class Animal {} class Dog: Animal {} let d = Dog() let k = d as Animal //在後面做轉換 ## Any and AnyObject ##(特殊資料型態) var list: [Any] = [] list.append("hello") list.append(10) list.append(20.3) list.append(["key":5])//字典型態 ## as! ## //取出資料 需要先轉成正確的型態 使用as! print((list[3] as! [String: Int])["key"]!) ``` ### for-in - for-in 陣列、數字、字串 - where 有條件的使用for-in - reverse() - strid函數使用 ```swift= //陣列 for city in ["台北","台中","高雄"]{ print(city) } //數字 不包含最後一個字 for n in 0...10{ print(n) } //字串 for c in "hello world"{ print(c) } ## 有條件的使用for-in ## //加入 where 做條件 for n in 0...10 where n%2 ==0{ print(n) }//結果0,2,4,6,8,10 //加入reverse使用 for n in (0...10).reversed() where n%2==0{ print(n) }//結果 10,8,6,4,2,0 //加入另外一個函數strid(from:to:by:) for n in stride(from: 10, to: -1, by: -2){ print(n) } //因為不包含to: 所以要10-0,才會to:-1 ``` ### While do{ ... }while() 變成 repeat{ ... }while ```swift= //Repeat-While 即 do while var n = 0 repeat { print(n) n = n+1 }while n<10 ``` 其他的都一樣 ### if 都一樣 ### Switch-case - case範圍 - 0...2 - enum使用 ``` swift= let n =10 switch n{ case 0...2: //範圍在0-2之間 } ## enum ##(列舉型態) enum Direction{ case north case east case south case west case unknown } //舉例: var d:Direction = .south switch d{ case .north: print("朝北") case .east: print("朝東") ... default: print("無法確定方向") } //好處:型態名稱可以省略 //原本:Direction.south 變 .south ``` ``新`` ### guard guard:會先過guard檢查,沒有符合就跳出 ``` swift= //舉例: var n = 10 func somefunc(){ guard n>0 else{ print("n不符") return } print("ok") } 當然也可以用 if 辦到 :) ``` ### 不同版本的ios須執行不同的程式碼 ```swift= if #available(iOS 12,*){ //在iOS 12 以上的程式碼 }else if #available(iOS 11,*){ ... }else if #....{ .... } ``` ## Swift靈魂 ``新`` ### Nil **Swift 語言的靈魂** **! 跟 ?** ?(表示其值==有可能是nil==) ! (表示其值==不可能是nil==) > 沒有加上問號的,代表變數或常數裡面只存放一個==非Nil==的值 輸出看到Optional這個字 >代表這個變數或是常數有可能是nil也可能不是nil,==只是現在剛好不是nil而已== ``` swift= let n = Int("10") print(n) //Optional(10) ``` 一開始的變數、常數或函數傳回值在宣告就加上問號,後面所有要接的變數或常數其資料型態也要加上問號。 驚嘆號保證其值不會是nil, ```swift= //因為print出來的資料只會有非nil的部分,故沒有Optional字樣 let n = Int("10")! print(n) //10 //因為print出來的資料只會有非nil的部分,故沒有Optional字樣 let n = Int("10") print(n!) //10 ``` :::warning 補充Optional: Optional是一個enum型別,裡面定義了兩個狀況 - 一個名為none,用來放nil - 一個名為some,用來放非nil **驚嘆號用來取用some內的內容,如果取到nil,程式會當掉** ::: ``新`` ### if let 延續上面的問題,用來檢查右邊的值(n)是否為nil,如果不是,則宣告會給一個常數k(按照下面的例子),在if的範圍內k不用加! 理解上: ```swift= //理解版本 if let k = n{ //n不是nil }else{ //n是nil } ``` 實際上: ```swift= //為了避免多想一個名字,通常等號兩邊都會相等 if let n = n{ //... }else{ //.... } ``` ### 給個預設值 某個變數或是常數為nil,我們可以使用連續兩個問號??來設定一個預設值,例如當n為nil,x得到0 ```swift= let n = Int("ABC") //得到nil let x = n ?? 0 //因為x得到nil,故將其設一個預設值為0 ``` :::danger 未來有可能存入nil,宣告時就必須在資料型態後方加上問號,而取出其值時必須加入驚嘆號(避免為nil) ::: ## 函數 ### 宣告跟傳回值 #### 宣告: 以func為開頭 ```swift= func saveDatat(){ //.... } ``` ``新`` #### 傳回值: 函數後方加入「->」符號與傳回值型態,並在函數使用return將值傳回 ```swift= func getData() -> String{ //.... return "hi" } ``` ### 參數與參數名稱 argument label 給外部參考用 parameter name 給內部計算用 ```swift= // argument label: number | parameter name: value // ˇarg ˇpar func square(number value: Float) -> Float{ return value* value } let ans = square(number:5) //ans:25 //如果argument label 與 parameter name一樣時,可以將兩者合併 func square(value: Float) -> Float{ return value * value } ``` 如果不想加入argument label ```swift= //使用_ 作為代替argument label func square (_ value: Float) -> Float{ return value * value } ``` 如果遇到函數有兩個以上的參數 ```swift= //兩個參數的用逗號隔開 func add(_ value:Float, _ value2:Float) -> Float{ return value1 * value2 } val = add(3,5) //val:15 ``` ### Call by address 在資料型態前面加上==inout==該參數就是傳址呼叫 呼叫參數要加上& ```swift= //call by address func inc(_ value:inout Int){ value = value+1 } var n = 0 inc(&n) print(n) ``` ### 參數預設 在參數型態後面加入=加預設 ```swift= func hi(who: String = "anonymous"){ print("hi,\(who)") } hi() //印出hi, anonymous hi(who:"John")//印出 hi, John ``` ``新`` ### 參數不固定 使用...來宣告一個參數數量不固定的函數,傳進函數的參數會轉成陣列型態 ```swift= //計算1+2+3+4+5 func sum(_ values: Float ...) -> Float{ var total: Float = 0 for n in values{ total += n } return total } print(sum(1,2,3,4,5)) ``` ``新`` ### 閉包Closure 將一個程式區段當作函數的參數或是類別的屬性來傳遞 ```swift= func inc(value: Int) -> Int{ return value+1 } func dec(value: Int) -> Int{ return value-1 } func f(_ value:Int, op:(Int)->Int)->Int{ return op(value) } //可以看到f這個函數的第二個參數op,其資料型態為(Int)->Int //代表要放入另外一個函數,函數的類型是接收一個整數型態的參數,然後回傳一個整數型態 //接收(Int) -> Int回傳 print(f(10,op:inc)) //11 print(f(3,op:dec)) //2 ``` #### Inline closure 呼叫f()時,在op的位置就直接填inc或dec即可 如果inc或dec這樣的函數沒有預先實做並給予一個名字,而直接寫在參數op的位置,就稱inline closure ```swift= //重點在in,in後面接計算式 let n = f(5, op:{ (value) -> Int in return value * value }) //原本 func f(_ value:Int, op:(Int)->Int){ return op(value) //return 一個func } print(n) //25 ``` #### Trailing closures 在op位置的closure可以放到f()函數的最後方,稱trailing closure ```swift= //差異語法 f(5){ ... } vs. f(5,op:{ ... }) let n = f(5){ (value) -> Int in return value * value } print(n) //25 ``` ``新`` ### 使用$0作為closure中參數名稱 使用$0,$1,$2作為closure中代替參數名稱 ```swift= let n =f(5){ return $0 * $0 } print(n) //25 ``` ```swift= //completion是閉包,這個閉包接收兩個參數,回傳一個Void func doSomething(completion: (_ str: String, _ err:Error?) -> Void){ completion("hello", nil) } ``` :::danger 重要!常用! :arrow_down: 跟上面的搭配用 ::: ``重要`` ```swift= ///呼叫completion 將"hello"字串及nil回傳給呼叫者 doSomething { (str,err) in if err != nil{ print("失敗: \(err!.localizedDescription)") return } print(str) //印出hello } ``` ### 字串 字串為字元陣列,資料型態為String,以雙引號開始跟結束。 使用三個雙引號來支援多行字串,例如: ```swift= let s = """ aa bb cc """ ``` #### 兩個字串相加 - 使用+號 - append()函數 ```swift= let s = "abc" + "def" 或 var s = "abc" s.append("def") ``` #### 將字串與數字相加 要先將數字轉成字串才行 - String() - \\() 括號內除了可以放數字外,也可以放變數跟常數,容易把數字、變數或常數與原本的字串合併起來 ```swift= "abc" + String(10) 或 // \()為特殊指令 "abc\(10)" ``` #### 檢查是否為空字串 - s1 == "" - isEmpty{} ```swift= let str = "" //isEmpty 成立就會做{}的內容 if str.isEmpty{ print("str 為空字串") } ``` #### 字串取值 不能直接str[3] 這樣來取值 1. 需要先將字串轉換成陣列 - 損耗記憶體位置 2. 使用String.Index型態的特殊索引取值 像是: 1. ```swift= //將字串轉型成矩陣 let str = "Hello" var arr = Array(str) print(arr[4]) ``` 2. ```swift= //使用String.Index型態的特殊索引值 str[.init(encodedOffset: 4)] //o 或 str[str.index(str.startIndex, offsetBy: 4)] ``` ### 常用的屬性跟函數 1. .count 字串長度 ```swift= //str = "abcdef" print(str.count) // 6 ``` 2. startIndex、endIndex ```swift= print(str.startIndex.encodedOffset) // 0 print(str.endIndex.encodedOffset) // 6 ``` 3. index(_:offsetBy:) 索引值加上偏移量 ``` //從第二個字元開始第三個字元 //str.index(起點,offset:偏移量) let begin = str.index(str.startIndex, offset:1) let end = str.index(begin, offset:3) print(str[begin..<end]) //bcd 4. contains() ```swift= if str.contains("ddd"){ ... } ``` 5. replacingOccurences(of:with:) :字串取代 ```swift= if str.contains("cde"){ let newstr = str.replaceOccurences(of:"cde", with: "") print(newstr) //abf } ``` 6. trimmingCharacters(in:) :頭尾去空白 ```swift= var str = " \tabcdef \n\n " str = str.trimmingCharacters(in:.whitespaceAndNewlines) print(str) //abcdef ``` 7. uppercased()、lowercased():轉換大小寫 8. hasPrefix(_:)、hasSuffix(_:) :判斷字串是否由某的字串的開頭或結尾 ```swift= if str.hasPrefix("ab"){ //ab開頭 } if str.hasSuffix("ef"){ } ``` 9. split(separator:) :分割字串,分割後以矩陣型態傳回 ```swift= let str = "ab,cd,ef" let arr = str.split(seperator:",") ``` ##### 參考自ios13程式設計實戰