Swift

2018-02-24 15:18 更新

X分鐘速成Y

其中 Y=swift

源代碼下載:?learnswift-cn.swift

Swift 是 Apple 開發(fā)的用于 iOS 和 OS X 開發(fā)的編程語言。Swift 于2014年 Apple WWDC (全球開發(fā)者大會)中被引入,用以與 Objective-C 共存,同時對錯誤代碼更具彈性。Swift 由 Xcode 6 beta 中包含的 LLVM 編譯器編譯。

Swift 的官方語言教程?Swift Programming Language?可以從 iBooks 免費下載.

亦可參閱:Apple’s?getting started guide?——一個完整的Swift 教程

// 導(dǎo)入外部模塊
import UIKit

// 
// MARK: 基礎(chǔ)
//

// XCODE 支持給注釋代碼作標(biāo)記,這些標(biāo)記會列在 XCODE 的跳轉(zhuǎn)欄里,支持的標(biāo)記為
// MARK: 普通標(biāo)記
// TODO: TODO 標(biāo)記
// FIXME: FIXME 標(biāo)記

println("Hello, world")

// 變量 (var) 的值設(shè)置后可以隨意改變
// 常量 (let) 的值設(shè)置后不能改變
var myVariable = 42
let ?πΩ = "value" // 可以支持 unicode 變量名 
let π = 3.1415926
let myConstant = 3.1415926
let explicitDouble: Double = 70   // 明確指定變量類型為 Double ,否則編譯器將自動推斷變量類型
let weak = "keyword"; let override = "another keyword" // 語句之間可以用分號隔開,語句未尾不需要分號
let intValue = 0007 // 7
let largeIntValue = 77_000 // 77000
let label = "some text " + String(myVariable) // 類型轉(zhuǎn)換
let piText = "Pi = \(π), Pi 2 = \(π * 2)" // 格式化字符串

// 條件編譯
// 使用 -D 定義編譯開關(guān)
#if false
    println("Not printed")
    let buildValue = 3
#else
    let buildValue = 7
#endif
println("Build value: \(buildValue)") // Build value: 7

/*
    Optionals 是 Swift 的新特性,它允許你存儲兩種狀態(tài)的值給 Optional 變量:有效值或 None

    Swift 要求所有的 Optinal 屬性都必須有明確的值,如果為空,則必須明確設(shè)定為 nil

    Optional<T> 是個枚舉類型
*/
var someOptionalString: String? = "optional" // 可以是 nil
// 下面的語句和上面完全等價,上面的寫法更推薦,因為它更簡潔,問號 (?) 是 Swift 提供的語法糖
var someOptionalString2: Optional<String> = "optional"

if someOptionalString != nil {
    // 變量不為空
    if someOptionalString!.hasPrefix("opt") {
        println("has the prefix")
    }

    let empty = someOptionalString?.isEmpty
}
someOptionalString = nil

// 顯式解包 optional 變量
var unwrappedString: String! = "Value is expected."
// 下面語句和上面完全等價,感嘆號 (!) 是個后綴運算符,這也是個語法糖
var unwrappedString2: ImplicitlyUnwrappedOptional<String> = "Value is expected."

if let someOptionalStringConstant = someOptionalString {
    // 由于變量 someOptinalString 有值,不為空,所以 if 條件為真
    if !someOptionalStringConstant.hasPrefix("ok") {
        // does not have the prefix
    }
}

// Swift 支持可保存任何數(shù)據(jù)類型的變量
// AnyObject == id
// 和 Objective-C `id` 不一樣, AnyObject 可以保存任何類型的值 (Class, Int, struct, 等)
var anyObjectVar: AnyObject = 7
anyObjectVar = "Changed value to a string, not good practice, but possible."

/*
    這里是注釋

    /*
        支持嵌套的注釋
    */
*/

//
// Mark: 數(shù)組與字典(關(guān)聯(lián)數(shù)組)
//

/*
    Array 和 Dictionary 是結(jié)構(gòu)體,不是類,他們作為函數(shù)參數(shù)時,是用值傳遞而不是指針傳遞。
    可以用 `var` 和 `let` 來定義變量和常量。
*/

// Array
var shoppingList = ["catfish", "water", "lemons"]
shoppingList[1] = "bottle of water"
let emptyArray = [String]() // 使用 let 定義常量,此時 emptyArray 數(shù)組不能添加或刪除內(nèi)容
let emptyArray2 = Array<String>() // 與上一語句等價,上一語句更常用
var emptyMutableArray = [String]() // 使用 var 定義變量,可以向 emptyMutableArray 添加數(shù)組元素

// 字典
var occupations = [
    "Malcolm": "Captain",
    "kaylee": "Mechanic"
]
occupations["Jayne"] = "Public Relations"   // 修改字典,如果 key 不存在,自動添加一個字典元素
let emptyDictionary = [String: Float]() // 使用 let 定義字典常量,字典常量不能修改里面的值
let emptyDictionary2 = Dictionary<String, Float>() // 與上一語句類型等價,上一語句更常用
var emptyMutableDictionary = [String: Float]() // 使用 var 定義字典變量

//
// MARK: 控制流
//

// 數(shù)組的 for 循環(huán)
let myArray = [1, 1, 2, 3, 5]
for value in myArray {
    if value == 1 {
        println("One!")
    } else {
        println("Not one!")
    }
}

// 字典的 for 循環(huán)
var dict = ["one": 1, "two": 2]
for (key, value) in dict {
    println("\(key): \(value)")
}

// 區(qū)間的 loop 循環(huán):其中 `...` 表示閉環(huán)區(qū)間,即[-1, 3];`..<` 表示半開閉區(qū)間,即[-1,3)
for i in -1...shoppingList.count {
    println(i)
}
shoppingList[1...2] = ["steak", "peacons"]
// 可以使用 `..<` 來去掉最后一個元素

// while 循環(huán)
var i = 1
while i < 1000 {
    i *= 2
}

// do-while 循環(huán)
do {
    println("hello")
} while 1 == 2

// Switch 語句
// Swift 里的 Switch 語句功能異常強大,結(jié)合枚舉類型,可以實現(xiàn)非常簡潔的代碼,可以把 switch 語句想象成 `if` 的語法糖
// 它支持字符串,類實例或原生數(shù)據(jù)類型 (Int, Double, etc)
let vegetable = "red pepper"
switch vegetable {
case "celery":
    let vegetableComment = "Add some raisins and make ants on a log."
case "cucumber", "watercress":
    let vegetableComment = "That would make a good tea sandwich."
case let localScopeValue where localScopeValue.hasSuffix("pepper"):
    let vegetableComment = "Is it a spicy \(localScopeValue)?"
default: // 在 Swift 里,switch 語句的 case 必須處理所有可能的情況,如果 case 無法全部處理,則必須包含 default語句 
    let vegetableComment = "Everything tastes good in soup."
}

//
// MARK: 函數(shù)
//

// 函數(shù)是一個 first-class 類型,他們可以嵌套,可以作為函數(shù)參數(shù)傳遞

// 函數(shù)文檔可使用 reStructedText 格式直接寫在函數(shù)的頭部
/**
    A greet operation

    - A bullet in docs
    - Another bullet in the docs

    :param: name A name
    :param: day A day
    :returns: A string containing the name and day value.
*/
func greet(name: String, day: String) -> String {
    return "Hello \(name), today is \(day)."
}
greet("Bob", "Tuesday")

// 函數(shù)參數(shù)前帶 `#` 表示外部參數(shù)名和內(nèi)部參數(shù)名使用同一個名稱。
// 第二個參數(shù)表示外部參數(shù)名使用 `externalParamName` ,內(nèi)部參數(shù)名使用 `localParamName`
func greet2(#requiredName: String, externalParamName localParamName: String) -> String {
    return "Hello \(requiredName), the day is \(localParamName)"
}
greet2(requiredName:"John", externalParamName: "Sunday")    // 調(diào)用時,使用命名參數(shù)來指定參數(shù)的值

// 函數(shù)可以通過元組 (tuple) 返回多個值
func getGasPrices() -> (Double, Double, Double) {
    return (3.59, 3.69, 3.79)
}
let pricesTuple = getGasPrices()
let price = pricesTuple.2 // 3.79
// 通過下劃線 (_) 來忽略不關(guān)心的值
let (_, price1, _) = pricesTuple // price1 == 3.69
println(price1 == pricesTuple.1) // true
println("Gas price: \(price)")

// 可變參數(shù)
func setup(numbers: Int...) {
    // 可變參數(shù)是個數(shù)組
    let number = numbers[0]
    let argCount = numbers.count
}

// 函數(shù)變量以及函數(shù)作為返回值返回
func makeIncrementer() -> (Int -> Int) {
    func addOne(number: Int) -> Int {
        return 1 + number
    }
    return addOne
}
var increment = makeIncrementer()
increment(7)

// 強制進(jìn)行指針傳遞 (引用傳遞),使用 `inout` 關(guān)鍵字修飾函數(shù)參數(shù)
func swapTwoInts(inout a: Int, inout b: Int) {
    let tempA = a
    a = b
    b = tempA
}
var someIntA = 7
var someIntB = 3
swapTwoInts(&someIntA, &someIntB)
println(someIntB) // 7

//
// MARK: 閉包
//
var numbers = [1, 2, 6]

// 函數(shù)是閉包的一個特例

// 閉包實例
// `->` 分隔了閉包的參數(shù)和返回值
// `in` 分隔了閉包頭 (包括參數(shù)及返回值) 和閉包體
// 下面例子中,`map` 的參數(shù)是一個函數(shù)類型,它的功能是把數(shù)組里的元素作為參數(shù),逐個調(diào)用 `map` 參數(shù)傳遞進(jìn)來的函數(shù)。
numbers.map({
    (number: Int) -> Int in
    let result = 3 * number
    return result
})

// 當(dāng)閉包的參數(shù)類型和返回值都是己知的情況下,且只有一個語句作為其返回值時,我們可以簡化閉包的寫法
numbers = numbers.map({ number in 3 * number })
// 我們也可以使用 $0, $1 來指代第 1 個,第 2 個參數(shù),上面的語句最終可簡寫為如下形式
// numbers = numbers.map({ $0 * 3 })

print(numbers) // [3, 6, 18]

// 簡潔的閉包
numbers = sorted(numbers) { $0 > $1 }
// 函數(shù)的最后一個參數(shù)可以放在括號之外,上面的語句是這個語句的簡寫形式
// numbers = sorted(numbers, { $0 > $1 })

print(numbers) // [18, 6, 3]

// 超級簡潔的閉包,因為 `<` 是個操作符函數(shù)
numbers = sorted(numbers, < )

print(numbers) // [3, 6, 18]

//
// MARK: 結(jié)構(gòu)體
//

// 結(jié)構(gòu)體和類非常類似,可以有屬性和方法

struct NamesTable {
    let names = [String]()

    // 自定義下標(biāo)運算符
    subscript(index: Int) -> String {
        return names[index]
    }
}

// 結(jié)構(gòu)體有一個自動生成的隱含的命名構(gòu)造函數(shù)
let namesTable = NamesTable(names: ["Me", "Them"])
let name = namesTable[1]
println("Name is \(name)") // Name is Them

//
// MARK: 類
//

// 類和結(jié)構(gòu)體的有三個訪問控制級別,他們分別是 internal (默認(rèn)), public, private
// internal: 模塊內(nèi)部可以訪問
// public: 其他模塊可以訪問
// private: 只有定義這個類或結(jié)構(gòu)體的源文件才能訪問

public class Shape {
    public func getArea() -> Int {
        return 0;
    }
}

// 類的所有方法和屬性都是 public 的
// 如果你只是需要把數(shù)據(jù)保存在一個結(jié)構(gòu)化的實例里面,應(yīng)該用結(jié)構(gòu)體

internal class Rect: Shape {
    // 值屬性 (Stored properties)
    var sideLength: Int = 1

    // 計算屬性 (Computed properties)
    private var perimeter: Int {
        get {
            return 4 * sideLength
        }
        set {
            // `newValue` 是個隱含的變量,它表示將要設(shè)置進(jìn)來的新值
            sideLength = newValue / 4
        }
    }

    // 延時加載的屬性,只有這個屬性第一次被引用時才進(jìn)行初始化,而不是定義時就初始化
    // subShape 值為 nil ,直到 subShape 第一次被引用時才初始化為一個 Rect 實例
    lazy var subShape = Rect(sideLength: 4)

    // 監(jiān)控屬性值的變化。
    // 當(dāng)我們需要在屬性值改變時做一些事情,可以使用 `willSet` 和 `didSet` 來設(shè)置監(jiān)控函數(shù)
    // `willSet`: 值改變之前被調(diào)用
    // `didSet`: 值改變之后被調(diào)用
    var identifier: String = "defaultID" {
        // `willSet` 的參數(shù)是即將設(shè)置的新值,參數(shù)名可以指定,如果沒有指定,就是 `newValue`
        willSet(someIdentifier) {
            println(someIdentifier)
        }
        // `didSet` 的參數(shù)是已經(jīng)被覆蓋掉的舊的值,參數(shù)名也可以指定,如果沒有指定,就是 `oldValue`
        didSet {
            println(oldValue)
        }
    }

    // 命名構(gòu)造函數(shù) (designated inits),它必須初始化所有的成員變量,
    // 然后調(diào)用父類的命名構(gòu)造函數(shù)繼續(xù)初始化父類的所有變量。
    init(sideLength: Int) {
        self.sideLength = sideLength
        // 必須顯式地在構(gòu)造函數(shù)最后調(diào)用父類的構(gòu)造函數(shù) super.init
        super.init()
    }

    func shrink() {
        if sideLength > 0 {
            --sideLength
        }
    }

    // 函數(shù)重載使用 override 關(guān)鍵字
    override func getArea() -> Int {
        return sideLength * sideLength
    }
}

// 類 `Square` 從 `Rect` 繼承
class Square: Rect {
    // 便捷構(gòu)造函數(shù) (convenience inits) 是調(diào)用自己的命名構(gòu)造函數(shù) (designated inits) 的構(gòu)造函數(shù)
    // Square 自動繼承了父類的命名構(gòu)造函數(shù)
    convenience init() {
        self.init(sideLength: 5)
    }
    // 關(guān)于構(gòu)造函數(shù)的繼承,有以下幾個規(guī)則:
    // 1\. 如果你沒有實現(xiàn)任何命名構(gòu)造函數(shù),那么你就繼承了父類的所有命名構(gòu)造函數(shù)
    // 2\. 如果你重載了父類的所有命名構(gòu)造函數(shù),那么你就自動繼承了所有的父類快捷構(gòu)造函數(shù)
    // 3\. 如果你沒有實現(xiàn)任何構(gòu)造函數(shù),那么你繼承了父類的所有構(gòu)造函數(shù),包括命名構(gòu)造函數(shù)和便捷構(gòu)造函數(shù)
}

var mySquare = Square()
println(mySquare.getArea()) // 25
mySquare.shrink()
println(mySquare.sideLength) // 4

// 類型轉(zhuǎn)換
let aShape = mySquare as Shape

// 使用三個等號來比較是不是同一個實例
if mySquare === aShape {
    println("Yep, it's mySquare")
}

class Circle: Shape {
    var radius: Int
    override func getArea() -> Int {
        return 3 * radius * radius
    }

    // optional 構(gòu)造函數(shù),可能會返回 nil
    init?(radius: Int) {
        self.radius = radius
        super.init()

        if radius <= 0 {
            return nil
        }
    }
}

// 根據(jù) Swift 類型推斷,myCircle 是 Optional<Circle> 類型的變量
var myCircle = Circle(radius: 1)
println(myCircle?.getArea())    // Optional(3)
println(myCircle!.getArea())    // 3
var myEmptyCircle = Circle(radius: -1)
println(myEmptyCircle?.getArea())    // "nil"
if let circle = myEmptyCircle {
    // 此語句不會輸出,因為 myEmptyCircle 變量值為 nil
    println("circle is not nil")
}

//
// MARK: 枚舉
//

// 枚舉可以像類一樣,擁有方法

enum Suit {
    case Spades, Hearts, Diamonds, Clubs
    func getIcon() -> String {
        switch self {
        case .Spades: return "?"
        case .Hearts: return "?"
        case .Diamonds: return "?"
        case .Clubs: return "?"
        }
    }
}

// 當(dāng)變量類型明確指定為某個枚舉類型時,賦值時可以省略枚舉類型
var suitValue: Suit = .Hearts

// 非整型的枚舉類型需要在定義時賦值
enum BookName: String {
    case John = "John"
    case Luke = "Luke"
}
println("Name: \(BookName.John.rawValue)")

// 與特定數(shù)據(jù)類型關(guān)聯(lián)的枚舉
enum Furniture {
    // 和 Int 型數(shù)據(jù)關(guān)聯(lián)的枚舉記錄
    case Desk(height: Int)
    // 和 String, Int 關(guān)聯(lián)的枚舉記錄
    case Chair(brand: String, height: Int)

    func description() -> String {
        switch self {
        case .Desk(let height):
            return "Desk with \(height) cm"
        case .Chair(let brand, let height):
            return "Chair of \(brand) with \(height) cm"
        }
    }
}

var desk: Furniture = .Desk(height: 80)
println(desk.description())     // "Desk with 80 cm"
var chair = Furniture.Chair(brand: "Foo", height: 40)
println(chair.description())    // "Chair of Foo with 40 cm"

//
// MARK: 協(xié)議
// 與 Java 的 interface 類似
//

// 協(xié)議可以讓遵循同一協(xié)議的類型實例擁有相同的屬性,方法,類方法,操作符或下標(biāo)運算符等
// 下面代碼定義一個協(xié)議,這個協(xié)議包含一個名為 enabled 的計算屬性且包含 buildShape 方法
protocol ShapeGenerator {
    var enabled: Bool { get set }
    func buildShape() -> Shape
}

// 協(xié)議聲明時可以添加 @objc 前綴,添加 @objc 前綴后,
// 可以使用 is, as, as? 等來檢查協(xié)議兼容性
// 需要注意,添加 @objc 前綴后,協(xié)議就只能被類來實現(xiàn),
// 結(jié)構(gòu)體和枚舉不能實現(xiàn)加了 @objc 的前綴
// 只有添加了 @objc 前綴的協(xié)議才能聲明 optional 方法
// 一個類實現(xiàn)一個帶 optional 方法的協(xié)議時,可以實現(xiàn)或不實現(xiàn)這個方法
// optional 方法可以使用 optional 規(guī)則來調(diào)用
@objc protocol TransformShape {
    optional func reshaped()
    optional func canReshape() -> Bool
}

class MyShape: Rect {
    var delegate: TransformShape?

    func grow() {
        sideLength += 2

        // 在 optional 屬性,方法或下標(biāo)運算符后面加一個問號,可以優(yōu)雅地忽略 nil 值,返回 nil。
        // 這樣就不會引起運行時錯誤 (runtime error)
        if let allow = self.delegate?.canReshape?() {
            // 注意語句中的問號
            self.delegate?.reshaped?()
        }
    }
}

//
// MARK: 其它
//

// 擴(kuò)展: 給一個已經(jīng)存在的數(shù)據(jù)類型添加功能

// 給 Square 類添加 `Printable` 協(xié)議的實現(xiàn),現(xiàn)在其支持 `Printable` 協(xié)議
extension Square: Printable {
    var description: String {
        return "Area: \(self.getArea()) - ID: \(self.identifier)"
    }
}

println("Square: \(mySquare)")  // Area: 16 - ID: defaultID

// 也可以給系統(tǒng)內(nèi)置類型添加功能支持
extension Int {
    var customProperty: String {
        return "This is \(self)"
    }

    func multiplyBy(num: Int) -> Int {
        return num * self
    }
}

println(7.customProperty) // "This is 7"
println(14.multiplyBy(3)) // 42

// 泛型: 和 Java 及 C# 的泛型類似,使用 `where` 關(guān)鍵字來限制類型。
// 如果只有一個類型限制,可以省略 `where` 關(guān)鍵字
func findIndex<T: Equatable>(array: [T], valueToFind: T) -> Int? {
    for (index, value) in enumerate(array) {
        if value == valueToFind {
            return index
        }
    }
    return nil
}
let foundAtIndex = findIndex([1, 2, 3, 4], 3)
println(foundAtIndex == 2) // true

// 自定義運算符:
// 自定義運算符可以以下面的字符打頭:
//      / = - + * % < > ! & | ^ . ~
// 甚至是 Unicode 的數(shù)學(xué)運算符等
prefix operator !!! {}

// 定義一個前綴運算符,使矩形的邊長放大三倍
prefix func !!! (inout shape: Square) -> Square {
    shape.sideLength *= 3
    return shape
}

// 當(dāng)前值
println(mySquare.sideLength) // 4

// 使用自定義的 !!! 運算符來把矩形邊長放大三倍
!!!mySquare
println(mySquare.sideLength) // 12
以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號