Swift 溫故知新

仔細想了想,我學swift的時候還是我剛接觸ios開發的時候了,那時swift還是beta版,當時因爲考慮到時beta版,有許多東西經常變,還沒有定論,所以就沒有投入過多的精力去學習,後來工作也一直用object-c,沒有用到swift,所以swift算是荒廢了好久了。現在swift正式版本都已經出了好多版本了,已經有很多公司在用swift開發了項目了,所以覺得很有必要重新學習下swift,好好感受下這門新語言的魅力!

以下是本人閱讀swift文檔之後總結整理的一些知識點,以及一些個人覺得swift和object-c的不同之處和個人理解,如有解釋不到位或是有誤的地方,還請各位指出,大家相互學習!

//

//  main.swift

//  SwiftLearning

//

//  Created by kenny on 17/6/6.

//  Copyright © 2017 kenny. All rights reserved.

//


import Foundation


//swift 的語句末尾的;號是非必要的了,可有可無,和JavaScript一樣

let label = "The width is "

let width = 94

let widthLabel = label + String(width)//相比object-c字符串也可以直接相加得到另一個字符串了

let labelWidth = "the width is \(width)"//對於intfloatdouble等基本數據類型可以通過\()進行拼接


let singleString = "single string"


//使用“”“ ”“” 引用的字符串可以換行了,不過這是swift4的特性,我目前的xcode8.1的,對應的swift3版本,以下注釋代碼會報錯

//let quotation = """

//Even though there's whitespace to the left,

//the actual lines aren't indented.

//Except for this line.

//Double quotes (") can appear without being escaped.

//

//I still have \(apples + oranges) pieces of fruit.

//""";


//創建並初始化數組

var shoppingList = ["catfish", "water", "tulips", "blue paint"]

shoppingList[1] = "bottle of water"

//創建並初始化字典

var occupations = [

    "Malcolm": "Captain",

    "Kaylee": "Mechanic",

]

occupations["Jayne"] = "Public Relations"

//如果數組和字典的類型可以被推斷出來則可以通過以下方式設爲新的空對象,但是不能作爲初始化方法,如:var shoppingList = []var occupations = [:]是不行的,必須指明數據類型

shoppingList = []

occupations = [:]


//通過構造函數創建一個空的指明類型的數組或字典

let emptyArray = [String]()

let emptyDictionary = [String: Float]()


//for循環和if語句更簡潔了,省略了括號,另外不能直接用if (a)這樣的形式來判斷對象是否爲空了,因爲if後面必須爲bool類型,不過可以通過let a = b的形式來判斷b對象是否爲空,因爲bnil的話會返回false,當然a==nil的形式肯定沒有問題

let individualScores = [75, 43, 103, 87, 12]

var teamScore = 0

for score in individualScores {

    if score > 50 {

        teamScore += 3

    } else {

        teamScore += 1

    }

}


/*for循環可以循環字典了,JavaScript也可以for循環字典,不過只是遍歷字典的key而已,如下輸出的只是key而已:

 var dic = {"key":"kenny", "key1":"xiayu"}

 for (i in dic)

 {

 console.log(i)

 }

key

key1

 */

let interestingNumbers = [

    "Prime": [2, 3, 5, 7, 11, 13],

    "Fibonacci": [1, 1, 2, 3, 5, 8],

    "Square": [1, 4, 9, 16, 25],

]

var largest = 0

for (kind, numbers) in interestingNumbers {

    for number in numbers {

        if number > largest {

            largest = number

        }

    }

}

print(largest)


//for循環還可以如下這樣寫,這裏循環4次,分別是0123

var total = 0

for i in 0..<4 {

    total += i

}

print(total)//輸出結果爲6


var optionalString: String?//初始化一個可爲空string類型的變量,當然其他類型的變量也可以

print(optionalString == nil)

//對於可選變量進行方法調用,如對象爲nil則不會執行,這一定程度上提高了代碼的健壯性

optionalString?.append("a")



var optionalName: String?

var greeting = "Hello!"

//這樣可以判斷一個對象是否爲nil,和==nil左右一樣

if let name = optionalName {

    greeting = "Hello, \(name)"

    print(greeting)

}

else {

    print(greeting)

}


//??判斷前面的變量值是否爲nil,是的話使用??後面的值,和三目運算符類似,C#中也有該特性,用法和這個一樣

let nickName: String? = nil

let fullName: String = "John Appleseed"

let informalGreeting = "Hi \(nickName ?? fullName)"


//switch變得很強大了,不僅僅只支持integer類型了,也不僅僅用於判斷是否相等了,不需要break了,不過default分支是必須的,這極大程度的加強了switch的語法並加強了switch語句的功能性,具體用法如下:

let vegetable = "red pepper"

switch vegetable {

case "celery":

    print("Add some raisins and make ants on a log.")

case "cucumber", "watercress":

    print("That would make a good tea sandwich.")

case let x where x.hasSuffix("pepper"):

    print("Is it a spicy \(x)?")

default:

    print("Everything tastes good in soup.")

}


let arrary = ["a", "b"]

switch arrary {

case let x where x.count == 0:

    print("funny")

default:

    print("default")

}


//while do...while改成了如下形式

var n = 2

while n < 100 {

    n *= 2

}

print(n)


//do換成了repeat

var m = 2

repeat {

    m *= 2

} while m < 100

print(m)


//方法定義需要用到func關鍵字了,具體初始化形式如下:

func greet(person: String, day: String) -> String {

    return "Hello \(person), today is \(day)."

}

print(greet(person: "kenny", day: "2017-06"))


//以下定義形式參數輸入提示變成了這樣 greet(<#T##person: String##String#>, on: <#T##String#>),

func greet(_ person: String, on day: String) -> String {

    return "Hello \(person), today is \(day)."

}


//Oject-c之前並不支持函數有多個返回值,只能通過字典了替換實現,現在swift支持多個返回值了,如下:

func calculateStatistics(scores: [Int]) -> (min: Int, max: Int, sum: Int) {

    var min = scores[0]

    var max = scores[0]

    var sum = 0

    

    for score in scores {

        if score > max {

            max = score

        } else if score < min {

            min = score

        }

        sum += score

    }

    

    return (min, max, sum)

}

let statistics = calculateStatistics(scores: [5, 3, 100, 3, 9])

//換取對應返回值的方式有以下兩種,可以通過返回變量名獲取下標

print(statistics.sum)

print(statistics.2)


//支持了函數嵌套,嵌套方法的好處就是方便代碼的整合和管理,需要注意的是被嵌套的函數屬於私有方法,不支持外部調用

func returnFifteen() -> Int {

    var y = 10

    func add() {

        y += 5

    }

    add()

    return y

}

let result = returnFifteen()


//函數也可以作爲返回值返回,返回值可以直接調用,是一個挺強大的功能,代碼的設計方式有多了幾分可變性

func makeIncrementer() -> ((Int) -> Int) {

    func addOne(number: Int) -> Int {

        return 1 + number

    }

    return addOne

}

var increment = makeIncrementer()

let result1 = increment(7)


//函數也可以作爲參數傳入

func hasAnyMatches(list: [Int], condition: (Int) -> Bool) -> Bool {

    for item in list {

        if condition(item) {

            return true

        }

    }

    return false

}

func lessThanTen(number: Int) -> Bool {

    return number < 10

}

var numbers = [20, 19, 7, 12]

let result3 = hasAnyMatches(list: numbers, condition: lessThanTen)


//可以直接傳一個函數閉包

let result4 = numbers.map({ (number: Int) -> Int in

    let result = 3 * number

    return result

})


//如果已知需要傳入的函數的參數類型和返回類型甚至可以省略參數和返回值的類型,對於單語句閉包隱式返回它們唯一語句的值。可做如下簡化

var mappedNumbers = numbers.map({ number in 3 * number })

//一個閉包作爲函數的最後一個參數可以在括號之後立即出現。如:

mappedNumbers = numbers.map(){number in 3 * number}

//當閉包是函數的惟一參數時,可以完全省略括號。還可以省略參數名,直接根據數字來引用每個參數($0$1$2......

let sortedNumbers = numbers.sorted { $0 > $1 }//從大到小排序

//還可以進一步簡化,你敢信?以下排序結果同上,不過過度簡化會使可讀性下降,個人覺得不應該太過追求簡化

let numbersSorted2 = numbers.sorted(by: >)


//類的聲明,對象的實例化,以及屬性及方法的調用

class Shape {

    var numberOfSides = 0

    func simpleDescription() -> String {

        return "A shape with \(numberOfSides) sides."

    }

}

//無論是屬性的設置和訪問還是方法的調用現在都是用.語法了

var shape = Shape()

shape.numberOfSides = 7

var shapeDescription = shape.simpleDescription()


class NamedShape {

    var numberOfSides: Int = 0

    var name: String

    //構造函數

    init(name: String) {

        self.name = name

    }

    func simpleDescription() -> String {

        return "A shape with \(numberOfSides) sides."

    }

}

//繼承及多態特性

class Square: NamedShape {

    //設置器和訪問器寫法

    var length:Double

    var perimeter: Double {

        get {

            return 3.0 * length

        }

        set {

            length = newValue / 3.0

        }

    }

    //還可以通過willsetdidset要控制參數的設置,比如說爲電話號碼加前綴去前綴等

    var triangle: Double {

        willSet {

            length = newValue * 3

        }

        didSet {

            length /= 3

        }

    }

    

    var sideLength: Double

    

    init(sideLength: Double, name: String) {

        /*

         A designated initializer must ensure that all of the properties introduced by its class are initialized before it delegates up to a superclass initializer.”

          the memory for an object is only considered fully initialized once the initial state of all of its stored properties is known. In order for this rule to be satisfied, a designated initializer must make sure that all of its own properties are initialized before it hands off up the chain.

         

         An initializer cannot call any instance methods, read the values of any instance properties, or refer to self as a value until after the first phase of initialization is complete.

         The class instance is not fully valid until the first phase ends. Properties can only be accessed, and methods can only be called, once the class instance is known to be valid at the end of the first phase.

         */

        //在調用super.init之前自身的屬性必須要有個默認值,因爲只有所有的屬性都有值了纔可以allocate內存

        //至於爲什麼super.init之前可以通過self.xx訪問屬性,個人猜想是因爲這個時候self已經構建出來了,並且這幾個屬性並沒有設置器和訪問器,不存在訪問父類屬性的可能,所以允許通過self.xx訪問屬性

        self.length = 0

        self.triangle = 0

        self.sideLength = sideLength

        super.init(name: name)

        self.perimeter = 0//而對於有設置器和訪問器的屬性需要放在super.init後面,因爲文檔指出只有調用了super.init纔算初始化完,我個人認爲爲了防止在設置器訪問器訪問未初始化好的父類屬性,所以禁止了該操作

        numberOfSides = 4

    }

    

    func area() -> Double {

        return sideLength * sideLength

    }

    //重寫父類方法

    override func simpleDescription() -> String {

        return "A square with sides of length \(sideLength)."

    }

}


//新的枚舉,支持枚舉內定義方法,獲取枚舉的keyvaluec#也支持,object-c是不支持的

enum Rank: Int {

    case ace = 1

    case two, three, four, five, six, seven, eight, nine, ten

    case jack, queen, king

    func simpleDescription() -> String {

        switch self {

        case .ace:

            return "ace"

        case .jack:

            return "jack"

        case .queen:

            return "queen"

        case .king:

            return "king"

        default:

            return String(self.rawValue)

        }

    }

}

let ace = Rank.ace

let aceRawValue = ace.rawValue

let r = Rank.init(rawValue: 1)//使用枚舉構造函數可以獲取對應值的枚舉,這裏獲取到的結果和let ace = Rank.ace一樣

print(aceRawValue)


//枚舉還有這種傳參用法,驚咯!!!!

enum ServerResponse {

    case result(String, String)

    case failure(String)

}


let success = ServerResponse.result("6:00 am", "8:09 pm")

let failure = ServerResponse.failure("Out of cheese.")

switch success {

case let .result(sunrise, sunset):

    print("Sunrise is at \(sunrise) and sunset is at \(sunset).")

case let .failure(message):

    print("Failure...  \(message)")

}


//結構體,也支持內部定義方法

struct Card {

    var suit: Rank

    var rank: Rank

    func simpleDescription() -> String {

        return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"

    }

}

let threeOfSpades = Card(suit: .jack, rank: .ace)

let threeOfSpadesDescription = threeOfSpades.simpleDescription()


//協議,枚舉,結構體,類均可以遵循協議,需要注意的是結構體遵循協議時需要在方法前面加mutating關鍵字表明該方法是可繼承修改的,而類並不需要,因爲類的方法都是可以繼承修改的

protocol ExampleProtocol {

    var simpleDescription: String { get }

    mutating func adjust()

}


//延展

extension Int: ExampleProtocol {

    var simpleDescription: String {

        return "The number \(self)"

    }

    mutating func adjust() {

        self += 42

    }

}

print(7.simpleDescription)


//異常處理

enum PrinterError: Error {

    case outOfPaper

    case noToner

    case onFire

}


func send(job: Int, toPrinter printerName: String) throws -> String {

    if printerName == "Never Has Toner" {

        throw PrinterError.noToner

    }

    return "Job sent"

}


do {

    let printerResponse = try send(job: 1040, toPrinter: "Bi Sheng")

    print(printerResponse)

} catch {

    print(error)

}


//catch 可以區分各種異常

do {

    let printerResponse = try send(job: 1440, toPrinter: "Gutenberg")

    print(printerResponse)

} catch PrinterError.onFire {

    print("I'll just put this over here, with the rest of the fire.")

} catch let printerError as PrinterError {

    print("Printer error: \(printerError).")

} catch {

    print(error)

}


//也可以使用try?來捕獲異常,當拋出異常時,值爲nil

let printerSuccess = try? send(job: 1884, toPrinter: "Mergenthaler")

let printerFailure = try? send(job: 1885, toPrinter: "Never Has Toner")


var fridgeIsOpen = false

let fridgeContent = ["milk", "eggs", "leftovers"]


//在方法裏面可以寫defer塊做一些必要的處理,defer裏面的代碼不管是否異常都會執行

func fridgeContains(_ food: String) -> Bool {

    fridgeIsOpen = true

    defer {

        fridgeIsOpen = false

    }

    

    let result = fridgeContent.contains(food)

    return result

}

let res = fridgeContains("banana")

print(fridgeIsOpen)


//泛型,方法,類,枚舉,結構體均可以使用泛型

func makeArray<Item>(repeating item: Item, numberOfTimes: Int) -> [Item] {

    var result = [Item]()

    for _ in 0..<numberOfTimes {

        result.append(item)

    }

    return result

}

let v = makeArray(repeating: "knock", numberOfTimes: 4)


//使用where限制泛型的條件

func anyCommonElements<T: Sequence, U: Sequence>(_ lhs: T, _ rhs: U) -> Bool

    where T.Iterator.Element: Equatable, T.Iterator.Element == U.Iterator.Element {

        for lhsItem in lhs {

            for rhsItem in rhs {

                if lhsItem == rhsItem {

                    return true

                }

            }

        }

        return false

}

let resu = anyCommonElements([1, 2, 3], [3])


以上便是本人這幾天對swift的學習內容,如有解釋錯誤或不到位的地方,請指出,謝謝!


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章