Swift 讀書筆記(ㄧ)

Connections

  • Array
  • Array with closure
  • Dictionary
  • Set

Array

我們要如何創建一個Array呢

有三種方法可以創建一個空Array

var array: Array<Int> = Array<Int>()
var array: [Int] = []  
 var array1: = array  

也可以創建時同時賦與初始值

var threeInts = [Int](repeating: 1, count:3)
// [1, 1, 1]
var ints = [1, 2, 3]
// [1, 2, 3]
var mergeArray = threeInts + ints
// [1, 1, 1, 1, 2, 3]

當然我們也可以像Objective-C一樣使用count 來知道有幾個元素

也可以用isEmpty 來查詢array是否為空

mergeArray.count // 6
mergeArray.isEmpty // false

基本上,在swift 中, 我們不建議直接使用索引值去取值,例如

mergeArray[2] // 1

我們可以使用

type(of: mergeArray) // Array<Int>.Type

他回傳的是一個Array type 而不是optional type

回傳 optional type 代表的意思是查找不到時會回傳為 nil
這是一個swift保護的措施。


回傳直接的type代表他取不到想要的值時,程式會直接崩潰。
array 的 remove(at:) , removeLast() 都是這種,

也代表你要自行保證使用Array 安全,要移除最後一個元素應該優先使用popLast()。

遍歷Array

我們有三種方式可以遍歷各種Connection 或Sequence

for in
for element in mergeArray {
    print(element)
}

for tulpe in array.enumerated() 
for (inx, value) in mergeArray.enumerated() {
    print("\(idx) : \(value)")
}

array.forEach { closure }
merge.forEach { print($0) }

Different With Array and NSArray

簡單講兩個差別就是

Swift中,可不可變數組是用let var 關鍵字聲名

Objective-C中,是以NSArray, NSMutableArray 決定

Swift中,如果你複製了一個Array,他使用一個叫做copy on write的基制,只在你再次修改Array時,他才會執行copy這個動作。

ex: 
var a = [1, 2, 3]
let b = a 
a.append(4)
//a [1, 2, 3, 4]
//b [1, 2, 3] 
//實際可以下斷點看pointer

Objectve-C中,如果把一個NSMutableArray賦值給一個新的NSArray

這時去修改原本Array,新的Array中的元素也會跟著改變,因為NSArray中元素是引用關係的。

ex:
let a = NSMutableArray(array: [1, 2, 3])
let copyB = a
a.insert(10, at:0)
//a and copyB = [10, 1, 2, 3]

要真的讓想到Array不同時m需要使用copy 關鍵字
let copyC = a.copy() as! NSArray

但是注意,裡面的element 還是指向同一個pointer,(詳情可以去看之前講deepCopy的文章

Array with closure

前面說過,針對Array操作,使用subScript和C語言中的for loop都不是好的選擇。Swift有一套自己的方法就是使用closure操作數組。

假設我們有個數組,想要計算每個值的平方該怎麼做呢,可能會這樣寫

var array = [1, 2, 3, 4, 5]
var squares = [Int]()

for value in array {
    squares.append(value * value)
}
print(squares) // [1, 4, 9, 16, 25]

我們可以使用map 函數
let squares = array.map {$0 * $0}

map 函數並不是什麼難懂的東西,它無非就是把for循環中的邏輯,封裝在一個函數裡,並返回一個新的。
Swift library中已經為我們準備許多好用的函數

array.min() // 1 optional type
array.max() // 5 optional type
array.filter { $0 * 2 == 1} // [1, 3, 5]
//只返回條件為true時

array.elementEqual([1, 2, 3], by: {$0 == $1})
// false $0 代表array的元素, $1 代表[1, 2, 3]中的元素

array.starts(with: [1, 2, 3], by: {$0 == $1})
//true 

array.sorted() , array,sorted(by: >)

array.reduce(0, +) //15 0代表除了array外帶入的初始值計算

swift中Array並沒有惕除元素中的方法,我們可以自己寫一個extension

extension Array {
    func reject(_ clousre: (Element) -> Bool) -> [Element] {
        return filter { !closure($0) 
    }
}

array.reject { $0 % 2 == 0} // [1, 3, 5] 

flatMap

flatMap這函數用處就是如果你使用map中的closure不返回一個數組元素,而是也返回一個數組, 這樣你就會得到一個二階陣列,這顯然不是我們需要的,這時flatMap就派上用唱

let animals = ["cat", "dog", "pig"]
let ids = [1, 2, 3]

let aa = animals.map { (ani) -> [(String,Int)] in
    let bb = ids.map({ (id) -> (String,Int) in
        return (ani, id)
        })
    return bb
}

簡化如下 
animals.map {animal in 
    return ids.map {id in (animal, id)}
}
// [[("cat", 1), ("cat", 2), ("cat", 3)], [("dog", 1), ("dog", 2), ("dog", 3)], [("pig", 1), ("pig", 2), ("pig", 3)]]

改成
animals.flatMap {animal in 
    return ids.map {id in (animal, id)}
}    
//[("cat", 1), ("cat", 2), ("cat", 3), ("dog", 1), ("dog", 2), ("dog", 3), ("pig", 1), ("pig", 2), ("pig", 3)]

Dictionary

其實dictionary 相關操作跟Objective-C差不多就不多講。

值得注意的是要當Dictionary的key必須是可以計算hash()的。例如Int, String, Float, Double, Bool, Date … 等等

如果是我們custom的 struct呢,swift都是通過protocol去遵從某些規格,所以我們要遵循HashAble protocol

假設我們有如下struct

struct Profile {
    var name: String
    var height: Int
}
let profile = Profile(name :"Jack", height: 170)
let person:[Profile: Int] = [profile: 300] // error Profile does not conform to protocol 'Hashable'

要解決這也很簡單,幫Profile加一個extension 並遵從Hashable
extension Profile:Hashable {
    var hashValue: Int {
        return name.hashValue ^ height.hashValue
       }
}
解決了後,編譯器又提示我們沒有遵從Equatable protocol 我們把上面的extension改成

extension Profile:Hashable,Equatable {
       var hashValue: Int {
        return name.hashValue ^ height.hashValue
    }

       static func == (lhs: Profile, rhs: Profile) -> Bool {
        return lhs.name == rhs.name && lhs.height == rhs.height
       } // 在swift中, 運算符必須要定義成static 方法
}

這樣,我們自定義的struct就可以放進Dictionary 中當key用了。

Set 懶的寫了 囧….