Swift 語法概覽(二)

7. 枚舉

枚舉是一組相同數(shù)據(jù)類型集合,Swift 中,枚舉可以添加計算屬性,實例方法,初始化方法等,類似 Class 行為。

enum CompassPoint {
    case north, south
    case east, west
}
var directionToHead = CompassPoint.west

// 類型推導(dǎo)
directionToHead = .east
switch directionToHead {
case .north:
    print("Lots of planets have a north")
case .east:
    print("Where the sun rises")
default:
    print("Not a safe place for humans")
}

Swift 中沒有默認整數(shù)值,上面4個不是默認(0,1,2,3)

枚舉迭代器

enum Beverage: CaseIterable {
    case coffee, tea, juice
}
for beverage in Beverage.allCases {
    print(beverage)
}
// coffee tea juice

關(guān)聯(lián)值

關(guān)聯(lián)值可以是任意類型,并且每個 Case 可以是不同類型的關(guān)聯(lián)值

enum Barcode {
    case upc(Int, Int, Int, Int)
    case qrCode(String)
}
var productBarcode = Barcode.upc(8, 85909, 51226, 3)
productBarcode = .qrCode("ABCDEFGHIJKLMNOP")

switch productBarcode {
case .upc(let numberSystem, let manufacturer, let product, let check):
    print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).")
case let .qrCode(productCode):
    print("QR code: \(productCode).")
}
// Prints "QR code: ABCDEFGHIJKLMNOP."

原始值

通過繼承,枚舉 Case 可以有默認值(原始值),所有 Case 都是相同類型,可以是字符串,字符,整數(shù),浮點數(shù),每個原始值都是不想等的。

enum ASCIIControlCharacter: Character {
    case tab = "\t"
    case lineFeed = "\n"
    case carriageReturn = "\r"
}

隱式原始值

Int 和 String 類型的原始值有隱式模式值

// Int 默認為 0,后面 + 1
enum Planet: Int {
    case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
}
// String 默認值和 Case 名相等
enum CompassPoint: String {
    case north, south, east, west
}

let earthsOrder = Planet.earth.rawValue
// earthsOrder is 3
let sunsetDirection = CompassPoint.west.rawValue
// sunsetDirection is "west"

// 初始化,返回可選類型
let possiblePlanet = Planet(rawValue: 7)
// possiblePlanet is of type Planet? and equals Planet.uranus

遞歸枚舉

枚舉 Case 的關(guān)聯(lián)值是當前枚舉的實例,嵌套枚舉值在 case 前面加 indirect 關(guān)鍵字。

enum ArithmeticExpression {
    case number(Int)
    indirect case addition(ArithmeticExpression, ArithmeticExpression)
    indirect case multiplication(ArithmeticExpression, ArithmeticExpression)
}
// 也可以這樣寫
indirect enum ArithmeticExpression {
    case number(Int)
    case addition(ArithmeticExpression, ArithmeticExpression)
    case multiplication(ArithmeticExpression, ArithmeticExpression)
}

let five = ArithmeticExpression.number(5)
let four = ArithmeticExpression.number(4)
let sum = ArithmeticExpression.addition(five, four)
let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2))

估值函數(shù)

func evaluate(_ expression: ArithmeticExpression) -> Int {
    switch expression {
    case let .number(value):
        return value
    case let .addition(left, right):
        return evaluate(left) + evaluate(right)
    case let .multiplication(left, right):
        return evaluate(left) * evaluate(right)
    }
}

print(evaluate(product))
// Prints "18"

8. 結(jié)構(gòu)體和類

Swift 中類和結(jié)構(gòu)體有很很多相似的功能,他們之間的主要區(qū)別是:

  1. 類可以繼承
  2. 類實例類型推導(dǎo)
  3. 析構(gòu)方法
  4. 類是引用類型,結(jié)構(gòu)體是值類型
struct Resolution {
    var width = 0
    var height = 0
}
class VideoMode {
    var resolution = Resolution()
    var interlaced = false
    var frameRate = 0.0
    var name: String?
}

let someResolution = Resolution()
let someVideoMode = VideoMode()
print("The width of someResolution is \(someResolution.width)")
print("The width of someVideoMode is \(someVideoMode.resolution.width)")
someVideoMode.resolution.width = 1280

結(jié)構(gòu)體類型有個自動生成的成員初始化方法,類沒有

let vga = Resolution(width: 640, height: 480)

屬性

屬性分為存儲屬性和計算屬性,存儲屬性存儲變量,常量,計算屬性計算值。

class DataImporter {
    var filename = "data.txt"
}
class DataManager {
    lazy var importer = DataImporter()
    var data = [String]()
}
let manager = DataManager();
// lazy 變量只有在使用的時候才會去初始化
// manager.import.fileName
計算屬性
struct Point {
    var x = 0.0, y = 0.0
}
struct Size {
    var width = 0.0, height = 0.0
}
struct Rect {
    var origin = Point()
    var size = Size()
    var center: Point {
        get {
            // 單條表達式隱藏 return
            Point(x: origin.x + (size.width / 2),
                  y: origin.y + (size.height / 2))
        }
        set(newCenter) { // 不提供 newCenter 參數(shù)則會提供一個默認參數(shù) newValue
            origin.x = newCenter.x - (size.width / 2)
            origin.y = newCenter.y - (size.height / 2)
        }
    }
    // 只讀計算屬性,沒有set,省略 get
    var volum: Double {
        return width * height * depth
    }
}
var square = Rect(origin: Point(x: 0.0, y: 0.0),
                  size: Size(width: 10.0, height: 10.0))
let initialSquareCenter = square.center
square.center = Point(x: 15.0, y: 15.0)
print("square.origin is now at (\(square.origin.x), \(square.origin.y))")
// Prints "square.origin is now at (10.0, 10.0)"
屬性觀察者
class StepCounter {
    var totalSteps: Int = 0 {
        willSet(newTotalSteps) {
            print("About to set totalSteps to \(newTotalSteps)")
        }
        didSet {
            if totalSteps > oldValue  {
                print("Added \(totalSteps - oldValue) steps")
            }
        }
    }
}
屬性封裝

定義封裝屬性

@propertyWrapper
struct TwelveOrLess {
    private var number: Int
    init() { self.number = 0 }
    var wrappedValue: Int {
        get { return number }
        set { number = min(newValue, 12) }
    }
}

使用封裝屬性

struct SmallRectangle {
    @TwelveOrLess var height: Int
    @TwelveOrLess var width: Int
}
// 等價于
struct SmallRectangle {
    private var _height = TwelveOrLess()
    private var _width = TwelveOrLess()
    var height: Int {
        get { return _height.wrappedValue }
        set { _height.wrappedValue = newValue }
    }
    var width: Int {
        get { return _width.wrappedValue }
        set { _width.wrappedValue = newValue }
    }
}

封裝屬性初始化

@propertyWrapper
struct SmallNumber {
    private var maximum: Int
    private var number: Int

    var wrappedValue: Int {
        get { return number }
        set { number = min(newValue, maximum) }
    }

    init() {
        maximum = 12
        number = 0
    }
    init(wrappedValue: Int) {
        maximum = 12
        number = min(wrappedValue, maximum)
    }
    init(wrappedValue: Int, maximum: Int) {
        self.maximum = maximum
        number = min(wrappedValue, maximum)
    }
}

struct ZeroRectangle {
    @SmallNumber var height: Int
    @SmallNumber var height: Int = 1
    @SmallNumber(wrappedValue: 2, maximum: 5) var height: Int
    @SmallNumber(maximum: 9) var width: Int = 2
}
ProjectedValue

封裝屬性提供的額外一個功能。

@propertyWrapper
struct SmallNumber {
    private var number: Int
    var projectedValue: Bool
    init() {
        self.number = 0
        self.projectedValue = false
    }
    var wrappedValue: Int {
        get { return number }
        set {
            if newValue > 12 {
                number = 12
                projectedValue = true
            } else {
                number = newValue
                projectedValue = false
            }
        }
    }
}
struct SomeStructure {
    @SmallNumber var someNumber: Int
}
var someStructure = SomeStructure()

someStructure.someNumber = 4
print(someStructure.$someNumber)
// Prints "false"

someStructure.someNumber = 55
print(someStructure.$someNumber)
// Prints "true"
類型屬性

類型公用的屬性,類似 C 中的靜態(tài)變量。

struct SomeStructure {
    static var storedTypeProperty = "Some value."
    static var computedTypeProperty: Int {
        return 1
    }
}
enum SomeEnumeration {
    static var storedTypeProperty = "Some value."
    static var computedTypeProperty: Int {
        return 6
    }
}
class SomeClass {
    static var storedTypeProperty = "Some value."
    static var computedTypeProperty: Int {
        return 27
    }
    // 使用 class 允許子類覆蓋
    class var overrideableComputedTypeProperty: Int {
        return 107
    }
}

方法

方法分為實例方法和類型方法

實例方法
class Counter {
    var count = 0
    func increment() {
        count += 1
        // 等價于 self.count += 1
    }
    func increment(by amount: Int) {
        count += amount
    }
    func reset() {
        count = 0
    }
}

let counter = Counter()
counter.increment()

結(jié)構(gòu)體,枚舉這種值類型數(shù)據(jù),默認函數(shù)不能修改屬性,如要修改,需要加關(guān)鍵字:mutating

struct Point {
    var x = 0.0, y = 0.0
    mutating func moveBy(x deltaX: Double, y deltaY: Double) {
        x += deltaX
        y += deltaY
    }
    mutating func moveBy(x deltaX: Double, y deltaY: Double) {
        self = Point(x: x + deltaX, y: y + deltaY)
    }
}
// 必須是 var 變量,不能 let
var somePoint = Point(x: 1.0, y: 1.0)

枚舉

enum TriStateSwitch {
    case off, low, high
    mutating func next() {
        switch self {
        case .off:
            self = .low
        case .low:
            self = .high
        case .high:
            self = .off
        }
    }
}
var ovenLight = TriStateSwitch.low
ovenLight.next()
// ovenLight is now equal to .high
ovenLight.next()
// ovenLight is now equal to .off
類型方法

static 修飾的方法,類類型可以使用 class 修飾以允許子類覆蓋該方法

class SomeClass {
    class func someTypeMethod() {
        // type method implementation goes here
    }
}
SomeClass.someTypeMethod()

struct LevelTracker {
    static var highestUnlockedLevel = 1
    var currentLevel = 1

    static func unlock(_ level: Int) {
        if level > highestUnlockedLevel { highestUnlockedLevel = level }
    }

    static func isUnlocked(_ level: Int) -> Bool {
        return level <= highestUnlockedLevel
    }

    // 取消不使用函數(shù)返回值時的編譯器警告
    @discardableResult 
    mutating func advance(to level: Int) -> Bool {
        if LevelTracker.isUnlocked(level) {
            currentLevel = level
            return true
        } else {
            return false
        }
    }
}

9. 下標語法

Swift 中類,結(jié)構(gòu)體,枚舉也可以使用下表語法

struct TimesTable {
    let multiplier: Int
    subscript(index: Int) -> Int {
        return multiplier * index
    }

    // 可以接收多個參數(shù)
    subscript(row: Int, column: Int) -> Double {
        get {
            assert(indexIsValid(row: row, column: column), "Index out of range")
            return grid[(row * columns) + column]
        }
        set {
            assert(indexIsValid(row: row, column: column), "Index out of range")
            grid[(row * columns) + column] = newValue
        }
    }

    // 靜態(tài)
    static subscript(n: Int) -> Planet {
        return Planet(rawValue: n)!
    }
}

let threeTimesTable = TimesTable(multiplier: 3)
print("six times three is \(threeTimesTable[6])")
// Prints "six times three is 18"

TimesTable[10]

10. 繼承

override 關(guān)鍵詞修飾

class Train: Vehicle {
    var a = 10
    // final 修飾的無法被繼承
    final var b = 20
    override func makeNoise() {
        super.makeNoise()
        print("Choo Choo")
    }
}

11. 初始化(構(gòu)造器)

不帶參數(shù)初始化方法

struct Fahrenheit {
    var temperature: Double
    init() {
        temperature = 32.0
    }
}
var f = Fahrenheit()

自定義帶參數(shù)初始化方法

struct Celsius {
    var temperatureInCelsius: Double
    init(fromFahrenheit fahrenheit: Double) {
        temperatureInCelsius = (fahrenheit - 32.0) / 1.8
    }
    init(fromKelvin kelvin: Double) {
        temperatureInCelsius = kelvin - 273.15
    }
}
let boilingPointOfWater = Celsius(fromFahrenheit: 212.0)
// boilingPointOfWater.temperatureInCelsius is 100.0
let freezingPointOfWater = Celsius(fromKelvin: 273.15)
// freezingPointOfWater.temperatureInCelsius is 0.0

指定初始化

指定初始化方法會初始化該類所有的存儲屬性以及完成父類初始化操作

便捷初始化

convenience init() {
}

原則

  1. 指定初始化方法必須調(diào)用直接父類的指定初始化方法
  2. 便捷初始化方法必須調(diào)用本類的其他初始化方法
  3. 便捷初始化方法必須最終調(diào)用本類的指定初始化方法

失敗的初始化

struct Animal {
    let species: String
    init?(species: String) {
        if species.isEmpty { return nil }
        self.species = species
    }
}

let someCreature = Animal(species: "Giraffe")
// someCreature is of type Animal?, not Animal
if let giraffe = someCreature {
    print("An animal was initialized with a species of \(giraffe.species)")
}
// Prints "An animal was initialized with a species of Giraffe"

必要的初始化

使用 required 修飾表示該初始化方法子類必須去實現(xiàn)

class SomeClass {
    required init() {
        // initializer implementation goes here
    }
}
class SomeSubclass: SomeClass {
    required init() {
        // subclass implementation of the required initializer goes here
    }
}

使用閉包(函數(shù))給屬性設(shè)置默認值

class SomeClass {
    let someProperty: SomeType = {
        // create a default value for someProperty inside this closure
        // someValue must be of the same type as SomeType
        return someValue
    }()
}

12. 析構(gòu)函數(shù)

析構(gòu)函數(shù)在對象釋放之前調(diào)用,使用 deinit 關(guān)鍵字,只有對象才有析構(gòu)

deinit {
    // perform the deinitialization
}

13. 類型反射

is

判斷對象是否是某個類或者其子類的實例

as

將對象轉(zhuǎn)化成某個類的實例,主要是父類向下轉(zhuǎn)子類(可能轉(zhuǎn)失?。?br> 當不確定是否能轉(zhuǎn)成功時使用 as?,否則使用 as!

class MediaItem {
    var name: String
    init(name: String) {
        self.name = name
    }
}
class Movie: MediaItem {
    var director: String
    init(name: String, director: String) {
        self.director = director
        super.init(name: name)
    }
}

class Song: MediaItem {
    var artist: String
    init(name: String, artist: String) {
        self.artist = artist
        super.init(name: name)
    }
}

let library = [
    Movie(name: "Casablanca", director: "Michael Curtiz"),
    Song(name: "Blue Suede Shoes", artist: "Elvis Presley"),
    Movie(name: "Citizen Kane", director: "Orson Welles"),
    Song(name: "The One And Only", artist: "Chesney Hawkes"),
    Song(name: "Never Gonna Give You Up", artist: "Rick Astley")
]

var movieCount = 0
var songCount = 0

for item in library {
    if item is Movie {
        movieCount += 1
    } else if item is Song {
        songCount += 1
    }
}

print("Media library contains \(movieCount) movies and \(songCount) songs")
// Prints "Media library contains 2 movies and 3 songs"

for item in library {
    if let movie = item as? Movie {
        print("Movie: \(movie.name), dir. \(movie.director)")
    } else if let song = item as? Song {
        print("Song: \(song.name), by \(song.artist)")
    }
}

// Movie: Casablanca, dir. Michael Curtiz
// Song: Blue Suede Shoes, by Elvis Presley
// Movie: Citizen Kane, dir. Orson Welles
// Song: The One And Only, by Chesney Hawkes
// Song: Never Gonna Give You Up, by Rick Astley

Any && AnyObject

Any 可以代表任意類型
AnyObject 代表任意類對象類型

var things = [Any]()

things.append(0)
things.append(3.14159)
things.append("hello")
things.append((3.0, 5.0))
things.append(Movie(name: "Ghostbusters", director: "Ivan Reitman"))
things.append({ (name: String) -> String in "Hello, \(name)" })

for thing in things {
    switch thing {
    case 0 as Int:
        print("zero as an Int")
    case 0 as Double:
        print("zero as a Double")
        print("some other double value that I don't want to print")
    case let someString as String:
        print("a string value of \"\(someString)\"")
    case let (x, y) as (Double, Double):
        print("an (x, y) point at \(x), \(y)")
    case let movie as Movie:
        print("a movie called \(movie.name), dir. \(movie.director)")
    case let stringConverter as (String) -> String:
        print(stringConverter("Michael"))
    default:
        print("something else")
    }
}

14. 擴展

類似于 OC 中的 Category,Swift 的 Extension 可以:

  1. 添加計算類型屬性
  2. 定義實例方法和類方法
  3. 提供新的初始化方法
  4. 定義下標
  5. 定義新的嵌套類型
  6. 遵循新的協(xié)議
extension SomeType : SomeProtocol, AnotherProtocol{

}

extension Double {
    var km: Double { return self * 1_000.0 }
    var m: Double { return self }
    var cm: Double { return self / 100.0 }
    var mm: Double { return self / 1_000.0 }
    var ft: Double { return self / 3.28084 }
}
let oneInch = 25.4.mm
print("One inch is \(oneInch) meters")
// Prints "One inch is 0.0254 meters"
let threeFeet = 3.ft
print("Three feet is \(threeFeet) meters")
// Prints "Three feet is 0.914399970739201 meters"

15. 協(xié)議

定義方法,屬性,有遵循該協(xié)議的類提供 具體的實現(xiàn)。

protocol SomeProtocol {
    var mustBeSettable: Int { get set }
    var doesNotNeedToBeSettable: Int { get }

    static func someTypeMethod()
    func random() -> Double
    mutating func toggle()
}
class SomeClass: SomeSuperclass, FirstProtocol, AnotherProtocol {
    weak var delegate: DiceGameDelegate?
}

16. 范型

func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
    let temporaryA = a
    a = b
    b = temporaryA
}

func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
    // function body goes here
}

func findIndex<T: Equatable>(of valueToFind: T, in array:[T]) -> Int? {
    for (index, value) in array.enumerated() {
        if value == valueToFind {
            return index
        }
    }
    return nil
}

// Associated Types in Action
protocol Container {
    associatedtype Item
    mutating func append(_ item: Item)
    var count: Int { get }
    subscript(i: Int) -> Item { get }
}

17. 不透明類型

英文太爛,文檔看得暈呼呼的,等什么時候靜下心來慢慢看,大概意思感覺有點類似 OC 中的類蔟

func makeOpaqueContainer<T>(item: T) -> some Container {
    return [item]
}
// 告訴你函數(shù)的返回值是某種 Container 類型

18. 自動引用計數(shù)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容