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ū)別是:
- 類可以繼承
- 類實例類型推導(dǎo)
- 析構(gòu)方法
- 類是引用類型,結(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() {
}
原則
- 指定初始化方法必須調(diào)用直接父類的指定初始化方法
- 便捷初始化方法必須調(diào)用本類的其他初始化方法
- 便捷初始化方法必須最終調(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 可以:
- 添加計算類型屬性
- 定義實例方法和類方法
- 提供新的初始化方法
- 定義下標
- 定義新的嵌套類型
- 遵循新的協(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 類型