10000 字|Swift语法全面解析

万字长文,每个知识点都对应了demo,理论与实践结合,讲解地很是到位。git

Swift介绍

Swift 是一门开发 iOS, macOS, watchOS 和 tvOS 应用的新语言。
swift 是一种安全,快速和互动的编程语言。
swift 支持代码预览(playgrounds),这个特性能够容许程序员在不编译和运行应用程序的前提下运行 Swift 代码并实时查看结果。程序员

Swift 经过采用现代编程模式来避免大量常见编程错误:express

  • 变量始终在使用前初始化。编程

  • 检查数组索引超出范围的错误。小程序

  • 检查整数是否溢出。swift

  • 可选值确保明确处理 nil 值。设计模式

  • 内存被自动管理。api

  • 错误处理容许从意外故障控制恢复。数组

基础部分

常量和变量

声明常量和变量, 常量和变量必须在使用前声明,使用 let 来声明常量,使用 var 来声明变量。
示例:安全

let maximumNumberOfLoginAttempts = 10
var currentLoginAttempt = 0

// 类型注解
var welcomeMessage: String

注释

单行注释双正斜杠(//), 多行注释(/* 多行的 */)。Swift 的多行注释能够嵌套在其它的多行注释之中。
示例:

// 这是一个注释

/* 这也是一个注释,
可是是多行的 */

/* 这是第一个多行注释的开头
/* 这是第二个被嵌套的多行注释 */
这是第一个多行注释的结尾 */

分号

Swift 并不强制要求你在每条语句的结尾处使用分号(;)。
同一行内写多条独立的语句必须用分号分隔。

let cat = "🐱"print(cat)
// 输出“🐱”

整数、浮点数

统一使用 Int 能够提升代码的可复用性,避免不一样类型数字之间的转换, 而且匹配数字的类型推断。
示例:

let minValue = UInt8.min  // minValue 为 0,是 UInt8 类型
let maxValue = UInt8.max  // maxValue 为 255,是 UInt8 类型

类型安全和类型推断

Swift 是一门类型安全的语言,这意味着 Swift 可让你清楚地知道值的类型。
若是你没有显式指定类型,Swift 会使用类型推断来选择合适的类型。(int、double)。
示例:

let meaningOfLife = 42
// meaningOfLife 会被推测为 Int 类型

let pi = 3.14159
// pi 会被推测为 Double 类型

数值型字面量、数值型类型转换

示例:

let decimalInteger = 17
let binaryInteger = 0b10001       // 二进制的17
let octalInteger = 0o21           // 八进制的17
let hexadecimalInteger = 0x11     // 十六进制的17

类型别名

类型别名(type aliases)就是给现有类型定义另外一个名字。你可使用 typealias 关键字来定义类型别名。
示例:

typealias AudioSample = UInt16
var maxAmplitudeFound = AudioSample.min
// maxAmplitudeFound 如今是 0

布尔值

示例:

let orangesAreOrange = true
let turnipsAreDelicious = false

元组

元组(tuples)把多个值组合成一个复合值。元组内的值能够是任意类型,并不要求是相同类型。
示例:

let http404Error = (404, "Not Found")
// http404Error 的类型是 (Int, String),值是 (404, "Not Found")

可选类型

使用可选类型(optionals)来处理值可能缺失的状况。可选类型表示两种可能:或者有值, 你能够解析可选类型访问这个值, 或者根本没有值。
示例:

var serverResponseCode: Int? = 404
// serverResponseCode 包含一个可选的 Int 值 404
serverResponseCode = nil
// serverResponseCode 如今不包含值

错误处理

错误处理,应对程序执行中可能会遇到的错误条件。
示例:

func makeASandwich() throws {
    // ...
}

do {
    try makeASandwich()
    eatASandwich()
} catch SandwichError.outOfCleanDishes {
    washDishes()
} catch SandwichError.missingIngredients(let ingredients) {
    buyGroceries(ingredients)
}

断言和先决条件

断言和先决条件,是在运行时所作的检查。

let age = -3
assert(age >= 0, "A person's age cannot be less than zero")
// 由于 age < 0,因此断言会触发

基本运算符

Swift 支持大部分标准 C 语言的运算符,还提供了 C 语言没有的区间运算符,例如 a..<b 或 a...b。

赋值运算符,算术运算符,组合赋值运算符,比较运算符,三元运算符,空合运算符,区间运算符,逻辑运算符

运算符分为一元、二元和三元运算符。
闭区间运算符(a...b)定义一个包含从 a 到 b(包括 a 和 b)的全部值的区间。
半开区间运算符(a..<b)定义一个从 a 到 b 但不包括 b 的区间。
闭区间操做符有另外一个表达形式,能够表达往一侧无限延伸的区间,(a...,...b)。
示例:

let names = ["Anna""Alex""Brian""Jack"]
let count = names.count
for i in 0..<count {
    print("第 \(i + 1) 我的叫 \(names[i])")
}
// 第 1 我的叫 Anna
// 第 2 我的叫 Alex
// 第 3 我的叫 Brian
// 第 4 我的叫 Jack

字符串和字符

字符串字面量,字符串插值,计算字符数量,访问和修改字符串,子字符串,比较字符串

初始化空字符串,字符串可变性,字符串是值类型,链接字符串和字符(+,+=)。
使用字符,可经过 for-in 循环来遍历字符串,获取字符串中每个字符的值。
字符串插值是一种构建新字符串的方式,能够在其中包含常量、变量、字面量和表达式。能够在已有字符串中插入常量、变量、字面量和表达式从而造成更长的字符串。
Swift 提供了三种方式来比较文本值:字符串字符相等、前缀相等和后缀相等。
示例:

// 多行字符串字面量
let quotation = """ The White Rabbit put on his spectacles.  "Where shall I begin,
please your Majesty?" he asked. "Begin at the beginning," the King said gravely, "and go on
till you come to the end; then stop." """

// 下面两个字符串实际上是同样的
let singleLineString = "These are the same."
let multilineString = """ These are the same. """

// 字符串插值
let multiplier = 3
let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"
// message 是 "3 times 2.5 is 7.5"

// 计算字符数量
var word = "cafe"
print("the number of characters in \(word) is \(word.count)")
// 打印输出“the number of characters in cafe is 4”

var emptyString = ""               // 空字符串字面量
var anotherEmptyString = String()  // 初始化方法
// 两个字符串均为空并等价。

let catCharacters: [Character] = ["C""a""t""!"]
let catString = String(catCharacters)
print(catString)
// 打印输出:“Cat!”

集合类型

Swift 语言提供数组(Array)、集合(Set)和字典(Dictionary)三种基本的集合类型用来存储集合数据。数组是有序数据的集。集合是无序无重复数据的集。字典是无序的键值对的集。

集合的可变性,数组(Arrays),集合(Sets),集合操做,字典

数组使用有序列表存储同一类型的多个值。相同的值能够屡次出如今一个数组的不一样位置中。
集合用来存储相同类型而且没有肯定顺序的值。当集合元素顺序不重要时或者但愿确保每一个元素只出现一次时可使用集合而不是数组。
集合操做,能够高效地完成集合的一些基本操做,好比把两个集合组合到一块儿,判断两个集合共有元素,或者判断两个集合是否全包含,部分包含或者不相交。
字典是一种无序的集合,它存储的是键值对之间的关系,其全部键的值须要是相同的类型,全部值的类型也须要相同。每一个值(value)都关联惟一的键(key),键做为字典中这个值数据的标识符。
示例:

// 集合
var someInts = [Int]()
print("someInts is of type [Int] with \(someInts.count) items.")
// 打印“someInts is of type [Int] with 0 items.”

var threeDoubles = Array(repeating: 0.0, count: 3)
// threeDoubles 是一种 [Double] 数组,等价于 [0.0, 0.0, 0.0]

var anotherThreeDoubles = Array(repeating: 2.5, count: 3)
// anotherThreeDoubles 被推断为 [Double],等价于 [2.5, 2.5, 2.5]
var sixDoubles = threeDoubles + anotherThreeDoubles
// sixDoubles 被推断为 [Double],等价于 [0.0, 0.0, 0.0, 2.5, 2.5, 2.5]

// enumerated() 方法遍历数组
var shoppingList: [String] = ["Eggs""Milk"]
for (index, value) in shoppingList.enumerated() {
    print("Item \(String(index + 1)): \(value)")
}

控制流

For-In 循环,While 循环(Repeat-While),条件语句,控制转移语句,提早退出(guard),检测 API 可用性

像 if 语句同样,guard 的执行取决于一个表达式的布尔值。咱们可使用 guard 语句来要求条件必须为真时,以执行 guard 语句后的代码。不一样于 if 语句,一个 guard 语句老是有一个 else 从句,若是条件不为真则执行 else 从句中的代码。
Swift 内置支持检查 API 可用性,编译器使用 SDK 中的可用信息来验证咱们的代码中使用的全部 API 在项目指定的部署目标上是否可用。若是咱们尝试使用一个不可用的 API,Swift 会在编译时报错。
示例:

let names = ["Anna""Alex""Brian""Jack"]
for name in names {
    print("Hello, \(name)!")
}

let numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
for (animalName, legCount) in numberOfLegs {
    print("\(animalName)s have \(legCount) legs")
}

//  repeat-while 循环的通常格式
repeat {
    statements
} while condition


// 提早退出
func greet(person: [String: String]) {
    guard let name = person["name"else {
        return
    }

    print("Hello \(name)!")

    guard let location = person["location"else {
        print("I hope the weather is nice near you.")
        return
    }

    print("I hope the weather is nice in \(location).")
}
greet(person: ["name""John"])
// 输出“Hello John!”
// 输出“I hope the weather is nice near you.”
greet(person: ["name""Jane""location""Cupertino"])
// 输出“Hello Jane!”
// 输出“I hope the weather is nice in Cupertino.”

函数

函数的定义与调用,函数参数与返回值,函数参数标签和参数名称,函数类型,嵌套函数

可选元组返回类型。
定义一个输入输出参数时,在参数定义前加 inout 关键字。
示例:

// 函数
func greet(person: String) -> String {
    let greeting = "Hello, " + person + "!"
    return greeting
}

func greet(person: String, from hometown: String) -> String {
    return "Hello \(person)!  Glad you could visit from \(hometown)."
}
print(greet(person: "Bill", from: "Cupertino"))
// 打印“Hello Bill!  Glad you could visit from Cupertino.”

// 可选元组返回类型
func minMax(array: [Int]) -> (min: Int, max: Int)? {
    if array.isEmpty { return nil }
    var currentMin = array[0]
    var currentMax = array[0]
    for value in array[1..<array.count] {
        if value < currentMin {
            currentMin = value
        } else if value > currentMax {
            currentMax = value
        }
    }
    return (currentMin, currentMax)
}

// 隐式返回的函数
func greeting(for person: String) -> String {
    "Hello, " + person + "!"
}
print(greeting(for"Dave"))
// 打印 "Hello, Dave! // 参数标签 func greet(person: String, from hometown: String) -> String {     return "Hello \(person)!  Glad you could visit from \(hometown)." } print(greet(person: "Bill", from: "Cupertino")) // 打印“Hello Bill!  Glad you could visit from Cupertino.” 

闭包

闭包是自包含的函数代码块,能够在代码中被传递和使用。与一些编程语言中的匿名函数(Lambdas)比较类似。

闭包表达式,尾随闭包,值捕获,闭包是引用类型,逃逸闭包(@escaping),自动闭包

若是你须要将一个很长的闭包表达式做为最后一个参数传递给函数,将这个闭包替换成为尾随闭包的形式颇有用。
闭包能够在其被定义的上下文中捕获常量或变量。即便定义这些常量和变量的原做用域已经不存在,闭包仍然能够在闭包函数体内引用和修改这些值。
示例:

// 闭包表达式语法
{ (parameters) -> return type in
    statements
}

// 尾随闭包
let digitNames = [
    0: "Zero", 1: "One", 2: "Two",   3: "Three", 4: "Four",
    5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine"
]
let numbers = [16, 58, 510]
let strings = numbers.map {
    (number) -> String in
    var number = number
    var output = ""
    repeat {
        output = digitNames[number % 10]! + output
        number /= 10
    } while number > 0
    return output
}
// strings 常量被推断为字符串类型数组,即 [String]
// 其值为 ["OneSix""FiveEight""FiveOneZero"]

// 值捕获
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0
    func incrementer() -> Int {
        runningTotal += amount
        return runningTotal
    }
    return incrementer
}

// 自动闭包,延迟求值
var customersInLine = ["Chris""Alex""Ewa""Barry""Daniella"]
print(customersInLine.count)
// 打印出“5”

let customerProvider = { customersInLine.remove(at: 0) }
print(customersInLine.count)
// 打印出“5”

print("Now serving \(customerProvider())!")
// Prints "Now serving Chris!"
print(customersInLine.count)
// 打印出“4”

枚举

使用 enum 关键词来建立枚举而且把它们的整个定义放在一对大括号内。

枚举语法,使用 Switch 语句匹配枚举值,枚举成员的遍历,关联值,原始值(默认值),递归枚举(indirect)

能够定义 Swift 枚举来存储任意类型的关联值,每一个枚举成员的关联值类型能够各不相同。
示例:

// 枚举语法
enum SomeEnumeration {
    // 枚举定义放在这里
}

enum CompassPoint {
    case north
    case south
    case east
    case west
}

enum Planet {
    case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune
}

let somePlanet = Planet.earth
switch somePlanet {
case .earth:
    print("Mostly harmless")
default:
    print("Not a safe place for humans")
}
// 打印“Mostly harmless”

// 关联值
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 let .upc(numberSystem, manufacturer, product, check):
    print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).")
case let .qrCode(productCode):
    print("QR code: \(productCode).")
}
// 打印“QR code: ABCDEFGHIJKLMNOP.”

// 递归枚举
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))
// (5 + 4) * 2

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))
// 打印“18”

结构体和类

结构体和类对比,结构体和枚举是值类型,类是引用类型

结构体和类做为一种通用而又灵活的结构,成为了人们构建代码的基础。你可使用定义常量、变量和函数的语法,为你的结构体和类定义属性、添加方法。
示例:

// 类和结构体
struct SomeStructure {
    // 在这里定义结构体
}
class SomeClass {
    // 在这里定义类
}

struct Resolution {
    var width = 0
    var height = 0
}
class VideoMode {
    var resolution = Resolution()
    var interlaced = false
    var frameRate = 0.0
    var name: String?
}

属性

存储属性,计算属性,属性观察器,属性包装器,全局变量和局部变量,类型属性(static)

属性将值与特定的类、结构体或枚举关联。存储属性会将常量和变量存储为实例的一部分,而计算属性则是直接计算(而不是存储)值。计算属性能够用于类、结构体和枚举,而存储属性只能用于类和结构体。
属性观察器监控和响应属性值的变化,每次属性被设置值的时候都会调用属性观察器,即便新值和当前值相同的时候也不例外。

  • willSet 在新的值被设置以前调用

  • didSet 在新的值被设置以后调用

属性包装器在管理属性如何存储和定义属性的代码之间添加了一个分隔层。
类型属性也是经过点运算符来访问。可是,类型属性是经过类型自己来访问,而不是经过实例。
示例:

// 属性
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 {
            let centerX = origin.x + (size.width / 2)
            let centerY = origin.y + (size.height / 2)
            return Point(x: centerX, y: centerY)
        }
        set(newCenter) {
            origin.x = newCenter.x - (size.width / 2)
            origin.y = newCenter.y - (size.height / 2)
        }
    }
}
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))")
// 打印“square.origin is now at (10.0, 10.0)”

// 属性包装器
@propertyWrapper
struct TwelveOrLess {
    private var number = 0
    var wrappedValue: Int {
        get { return number }
        set { number = min(newValue, 12) }
    }
}

方法

实例方法(Instance Methods),类型方法(static)

方法是与某些特定类型相关联的函数。
类、结构体、枚举均可以定义实例方法;实例方法为给定类型的实例封装了具体的任务与功能。
类、结构体、枚举也能够定义类型方法;类型方法与类型自己相关联。
示例:

// 方法
class Counter {
    var count = 0
    func increment() {
        count += 1
    }
    func increment(by amount: Int) {
        count += amount
    }
    func reset() {
        count = 0
    }
}

下标

下标能够定义在类、结构体和枚举中,是访问集合、列表或序列中元素的快捷方式

下标语法(subscript),下标用法,下标选项,类型下标(static)

subscript(index: Int) -> Int {
    get {
      // 返回一个适当的 Int 类型的值
    }
    set(newValue) {
      // 执行适当的赋值操做
    }
}

// 示例
struct TimesTable {
    let multiplier: Int
    subscript(index: Int) -> Int {
        return multiplier * index
    }
}
let threeTimesTable = TimesTable(multiplier: 3)
print("six times three is \(threeTimesTable[6])")
// 打印“six times three is 18”

var numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
numberOfLegs["bird"] = 2

// 类型下标
enum Planet: Int {
    case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
    static subscript(n: Int) -> Planet {
        return Planet(rawValue: n)!
    }
}
let mars = Planet[4]
print(mars)

继承

定义一个基类,子类生成,重写(override),防止重写(final)

不继承于其它类的类,称之为基类。
示例:

// 继承
class SomeClass: SomeSuperclass {
    // 这里是子类的定义
}

class Vehicle {
    var currentSpeed = 0.0
    var description: String {
        return "traveling at \(currentSpeed) miles per hour"
    }
    func makeNoise() {
        // 什么也不作——由于车辆不必定会有噪音
    }
}

class Car: Vehicle {
    var gear = 1
    override var description: String {
        return super.description + " in gear \(gear)"
    }
}

class AutomaticCar: Car {
    override var currentSpeed: Double {
        didSet {
            gear = Int(currentSpeed / 10.0) + 1
        }
    }
}

构造过程

构造过程是使用类、结构体或枚举类型的实例以前的准备过程。

存储属性的初始赋值,自定义构造过程,默认构造器,值类型的构造器代理,类的继承和构造过程,可失败构造器,必要构造器(required)

构造器能够经过调用其它构造器来完成实例的部分构造过程。这一过程称为构造器代理,它能避免多个构造器间的代码重复。
Swift 为类类型提供了两种构造器来确保实例中全部存储型属性都能得到初始值,它们被称为指定构造器和便利构造器。
能够在一个类,结构体或是枚举类型的定义中,添加一个或多个可失败构造器。其语法为在 init 关键字后面添加问号(init?)。
必要构造器,在类的构造器前添加 required 修饰符代表全部该类的子类都必须实现该构造器。
示例:

// 构造过程
init() {
    // 在此处执行构造过程
}

struct Fahrenheit {
    var temperature: Double
    init() {
        temperature = 32.0
    }
}
var f = Fahrenheit()
print("The default temperature is \(f.temperature)° Fahrenheit")
// 打印“The default temperature is 32.0° Fahrenheit”

struct Color {
    let red, green, blue: Double
    init(red: Double, green: Double, blue: Double) {
        self.red   = red
        self.green = green
        self.blue  = blue
    }
    init(white: Double) {
        red   = white
        green = white
        blue  = white
    }
}

析构过程

析构器只适用于类类型,当一个类的实例被释放以前,析构器会被当即调用。析构器用关键字 deinit 来标示,相似于构造器要用 init 来标示。
Swift 会自动释放再也不须要的实例以释放资源。
示例:

// 析构过程
deinit {
    // 执行析构过程
}

class Bank {
    static var coinsInBank = 10_000
    static func distribute(coins numberOfCoinsRequested: Int) -> Int {
        let numberOfCoinsToVend = min(numberOfCoinsRequested, coinsInBank)
        coinsInBank -= numberOfCoinsToVend
        return numberOfCoinsToVend
    }
    static func receive(coins: Int) {
        coinsInBank += coins
    }
}

class Player {
    var coinsInPurse: Int
    init(coins: Int) {
        coinsInPurse = Bank.distribute(coins: coins)
    }
    func win(coins: Int) {
        coinsInPurse += Bank.distribute(coins: coins)
    }
    deinit {
        Bank.receive(coins: coinsInPurse)
    }
}

可选链式调用

可选链式调用是一种能够在当前值可能为 nil 的可选值上请求和调用属性、方法及下标的方法。
经过在想调用的属性、方法,或下标的可选值后面放一个问号(?),能够定义一个可选链。相似在可选值后面放一个叹号(!)来强制展开它的值。它们的主要区别在于当可选值为空时可选链式调用只会调用失败,然而强制展开将会触发运行时错误。
示例:

class Person {
    var residence: Residence?
}

class Residence {
    var numberOfRooms = 1
}
let john = Person()
let roomCount = john.residence!.numberOfRooms
// 这会引起运行时错误

if let roomCount = john.residence?.numberOfRooms {
    print("John's residence has \(roomCount) room(s).")
} else {
    print("Unable to retrieve the number of rooms.")
}
// 打印“Unable to retrieve the number of rooms.”

john.residence = Residence()

if let roomCount = john.residence?.numberOfRooms {
    print("John's residence has \(roomCount) room(s).")
} else {
    print("Unable to retrieve the number of rooms.")
}
// 打印“John's residence has 1 room(s).” 

错误处理

错误处理(Error handling) 是响应错误以及从错误中恢复的过程。Swift 在运行时提供了抛出、捕获、传递和操做可恢复错误(recoverable errors)的一等支持。

表示与抛出错误,处理错误,指定清理操做

在 Swift 中,错误用遵循 Error 协议的类型的值来表示。
Swift 中有 4 种处理错误的方式。能够把函数抛出的错误传递给调用此函数的代码(throws)、用 do-catch 语句处理错误、将错误做为可选类型处理(try?)、或者断言此错误根本不会发生(try!)。
defer 语句将代码的执行延迟到当前的做用域退出以前。
示例:

// 错误处理
enum VendingMachineError: Error {
    case invalidSelection                     //选择无效
    case insufficientFunds(coinsNeeded: Int) //金额不足
    case outOfStock                             //缺货
}
throw VendingMachineError.insufficientFunds(coinsNeeded: 5)

var vendingMachine = VendingMachine()
vendingMachine.coinsDeposited = 8
do {
    try buyFavoriteSnack(person: "Alice", vendingMachine: vendingMachine)
    print("Success! Yum.")
} catch VendingMachineError.invalidSelection {
    print("Invalid Selection.")
} catch VendingMachineError.outOfStock {
    print("Out of Stock.")
} catch VendingMachineError.insufficientFunds(let coinsNeeded) {
    print("Insufficient funds. Please insert an additional \(coinsNeeded) coins.")
} catch {
    print("Unexpected error: \(error).")
}
// 打印“Insufficient funds. Please insert an additional 2 coins.”

// 指定清理操做
func processFile(filename: String) throws {
    if exists(filename) {
        let file = open(filename)
        defer {
            close(file)
        }
        while let line = try file.readline() {
            // 处理文件。
        }
        // close(file) 会在这里被调用,即做用域的最后。
    }
}

类型转换

类型转换在 Swift 中使用 is 和 as 操做符实现。这两个操做符分别提供了一种简单达意的方式去检查值的类型或者转换它的类型。

为类型转换定义类层次,检查类型(is),向下转型(as? 或 as!),Any 和 AnyObject 的类型转换

能够将类型转换用在类和子类的层次结构上,检查特定类实例的类型而且转换这个类实例的类型成为这个层次结构中的其余类型。
Swift 为不肯定类型提供了两种特殊的类型别名:

  • Any 能够表示任何类型,包括函数类型。

  • AnyObject 能够表示任何类类型的实例。

示例:

// 类型转换
// 一个基类 MediaItem
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.srtist = artist
  super.init(name: name)
 }
}

let library = [
 Movie(name: "Casablanca", director: "Micheal Curtiz"),
 Song(name: "Blue Suede Shose", artist: "Elvis Presley"),
 Movie(name: "Citizen Kane", director: "Orson Wells"),
 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)")
// 打印“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

嵌套类型

Swift 容许定义嵌套类型,能够在支持的类型中定义嵌套的枚举、类和结构体。

嵌套类型实践,引用嵌套类型

要在一个类型中嵌套另外一个类型,将嵌套类型的定义写在其外部类型的 {} 内,并且能够根据须要定义多级嵌套。
示例:

// 嵌套类型
stuct BlackjackCard {
 // 嵌套的 Suit 枚举
 enum Suit: Character {
  case spades = "1", hearts = "2", diamonds = "3", clubs = "4"
 }
 
 // 嵌套的 Rank 枚举
 enum Rank: Int {
  case two = 2, three, four, five, six, seven, eight, nine, ten
  case jack, queen, king, ace
  struct Values {
   let first: Int, second: Int?
  }
  var values: Values {
   switch self {
   case .ace:
    return Values(first: 1, second: 11)
   case .jack, .queen, .king:
    return Values(first: 10, second: nil)
   default:
    return Values(first: self.rawValue, second: nil)
   }
  }
 }
 
 // BlackjackCard 的属性和方法
 let rank: Rank, suit: Suit
 var description: String {
  var output = "suit is \(suit.rawValue),"
  output += " value is \(rank.values.first)"
  if let second = rank.values.second {
   output += " or \(second)"
  }
  return output
 }
}

let theAceOfSpades = BlackjackCard(rank: .ace, suit: .spades)
print("theAceOfSpades: \(theAceOfSpades.description)")
// 打印“theAceOfSpades: suit is 1, value is 1 or 11”

let heartsSymbol = BlackjackCard.Suit.hearts.rawValue
// 2

扩展

扩展能够给一个现有的类,结构体,枚举,还有协议添加新的功能。

扩展的语法,计算型属性,构造器,方法,下标,嵌套类型

Swift 中的扩展能够:

  • 添加计算型实例属性和计算型类属性

  • 定义实例方法和类方法

  • 提供新的构造器

  • 定义下标

  • 定义和使用新的嵌套类型

  • 使已经存在的类型遵循(conform)一个协议

扩展语法:

extension SomeType {
  // 在这里给 SomeType 添加新的功能
}

扩展能够给现有类型添加计算型实例属性和计算型类属性。
扩展能够给现有的类型添加新的构造器。
扩展能够给现有类型添加新的实例方法和类方法。
扩展能够给现有的类型添加新的下标。
扩展能够给现有的类,结构体,还有枚举添加新的嵌套类型。
示例:

// 扩展的语法
extension SomeType {
  // 在这里给 SomeType 添加新的功能
}
// 添加一个或多个协议
extension SomeType: SomeProtocol, AnotherProtocol {
  // 协议所须要的实现写在这里
}

struct Size {
 var width = 0.0, height = 0.0
}
struct Point {
 var x = 0.0, y = 0.0
}
struct Rect {
 var origin = Point()
 var size = Size()
}

extension Rect {
 init(center: Point, size: Size) {
  let originX = center.x - (size.width / 2)
  let originY = center.y - (size.height / 3)
  self.init(origin: Point(x: originX, y: originY), size: size)
 }
}
let centerRect = Rect(center: Point(x: 4.0, y: 4.0), 
 size: Size(width: 3.0, height: 3.0))
// centerRect 的 origin 是 (2.5, 2.5) 而且它的 size 是 (3.0, 3.0)

extension Int {
 func repetitions(task: () -> Void) {
  for _ in 0..<self {
   task()
  }
 }
}
3.repetitions {
 print("Hello!")
}
// Hello!
// Hello!
// Hello!

extension Int {
 mutating func square() {
  self = self * self
 }
}
var somtInt = 3
someInt.square()
// someInt 如今是9

协议

协议定义了一个蓝图,规定了用来实现某一特定任务或者功能的方法、属性,以及其余须要的东西。
类、结构体或枚举均可以遵循协议,并为协议定义的这些要求提供具体实现。

协议语法,属性要求,方法要求,异变方法要求,构造器要求,协议做为类型,委托,协议类型的集合,协议的继承,类专属的协议,协议合成,检查协议一致性,可选的协议要求,协议扩展,

协议语法

protocol SomeProtocol {
    // 这里是协议的定义部分
}

协议能够要求遵循协议的类型提供特定名称和类型的实例属性或类型属性。
协议能够要求遵循协议的类型实现某些指定的实例方法或类方法。
在值类型(即结构体和枚举)的实例方法中,将 mutating 关键字做为方法的前缀,写在 func 关键字以前,表示能够在该方法中修改它所属的实例以及实例的任意属性的值。
协议能够要求遵循协议的类型实现指定的构造器。
委托是一种设计模式,它容许类或结构体将一些须要它们负责的功能委托给其余类型的实例。
示例:

// 协议语法
protocol SomeProtocol {
    // 这里是协议的定义部分
}

struct SomeStructure: FirstProtocol, AnotherProtocol {
 // 这里是结构体的定义部分
}

class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol {
 // 这里是类的定义部分
}

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

protocol AnotherProtocol {
 static var someTypeProperty: Int { get set }
}

protocol FullyNamed {
 var fullName: String { get }
}

struct person: FullyNamed {
 var fullName: String
}
let john = Person(fullName: "John Appleseed")
// john.fullName 为 "John Appleseed"

class Starship: FullyNamed {
 var prefix: String?
 var name: String
 init(name: String, prefix: String? = nil) {
  self.name = name
  self.prefix = prefix
 }
 
 var fullName: String {
  return (prefix != nil ? prefix! + " " : "") + name
 }
}
var ncc1701 = Starship(name: "Enterprise", prefix: "USS")
// ncc1701.fullName 为 "USS Enterprise"

泛型

泛型代码让你能根据自定义的需求,编写出适用于任意类型的、灵活可复用的函数及类型。
你可避免编写重复的代码,而是用一种清晰抽象的方式来表达代码的意图。

泛型函数,类型参数,命名类型参数,泛型类型,泛型扩展,类型约束,关联类型

示例:

func swapTwoInts(_ a: inout Int, _ b: inout Int) {
    let temporaryA = a
    a = b
    b = temporaryA
}

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

func swapTwoInts(_ a: inout Int, _ b: inout Int)
func swapTwoValues<T>(_ a: inout T, _ b: inout T)

var someInt = 3
var anotherInt = 107
swapTwoValues(&someInt, &anotherInt)
// someInt 如今是 107,anotherInt 如今是 3

var someString = "hello"
var anotherString = "world"
swapTwoValues(&someString, &anotherString)
// someString 如今是“world”,anotherString 如今是“hello”

不透明类型

具备不透明返回类型的函数或方法会隐藏返回值的类型信息。
函数再也不提供具体的类型做为返回类型,而是根据它支持的协议来描述返回值。

不透明类型解决的问题,返回不透明类型,不透明类型和协议类型的区别

在处理模块和调用代码之间的关系时,隐藏类型信息很是有用,由于返回的底层数据类型仍然能够保持私有。
不透明类型和泛型相反。不透明类型容许函数实现时,选择一个与调用代码无关的返回类型。
若是函数中有多个地方返回了不透明类型,那么全部可能的返回值都必须是同一类型。返回不透明类型和返回协议类型主要区别,就在因而否须要保证类型一致性。
一个不透明类型只能对应一个具体的类型,即使函数调用者并不能知道是哪种类型;协议类型能够同时对应多个类型,只要它们都遵循同一协议。
示例:

protocol Shape {
    func draw() -> String
}

struct Triangle: Shape {
    var size: Int
    func draw() -> String {
        var result = [String]()
        for length in 1...size {
            result.append(String(repeating: "*", count: length))
        }
        return result.joined(separator: "\n")
    }
}
let smallTriangle = Triangle(size: 3)
print(smallTriangle.draw())
// *
// **
// ***

struct FlippedShape<T: Shape>: Shape {
    var shape: T
    func draw() -> String {
        let lines = shape.draw().split(separator: "\n")
        return lines.reversed().joined(separator: "\n")
    }
}
let flippedTriangle = FlippedShape(shape: smallTriangle)
print(flippedTriangle.draw())
// ***
// **
// *

自动引用计数

Swift 使用自动引用计数(ARC)机制来跟踪和管理你的应用程序的内存。
若是两个类实例互相持有对方的强引用,于是每一个实例都让对方一直存在,就是这种状况。这就是所谓的循环强引用。
Swift提供了两种办法用来解决你在使用类的属性时所遇到的循环强引用问题:弱引用(weak reference)和无主引用(unowned reference)。

  • 声明属性或者变量时,在前面加上 weak 关键字代表这是一个弱引用。

  • 声明属性或者变量时,在前面加上关键字 unowned 表示这是一个无主引用。

示例:

// 自动引用计数实践
class Person {
    let name: String
    init(name: String) {
        self.name = name
        print("\(name) is being initialized")
    }
    deinit {
        print("\(name) is being deinitialized")
    }
}

var reference1: Person?
var reference2: Person?
var reference3: Person?
reference1 = Person(name: "John Appleseed")
// 打印“John Appleseed is being initialized”
reference2 = reference1
reference3 = reference1
reference1 = nil
reference2 = nil
reference3 = nil
// 打印“John Appleseed is being deinitialized”

// 循环强引用
class Person {
    let name: String
    init(name: String) { self.name = name }
    var apartment: Apartment?
    deinit { print("\(name) is being deinitialized") }
}

class Apartment {
    let unit: String
    init(unit: String) { self.unit = unit }
    var tenant: Person?
    deinit { print("Apartment \(unit) is being deinitialized") }
}
var john: Person?
var unit4A: Apartment?
john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")

john = nil
unit4A = nil

// 弱引用
class Person {
    let name: String
    init(name: String) { self.name = name }
    var apartment: Apartment?
    deinit { print("\(name) is being deinitialized") }
}

class Apartment {
    let unit: String
    init(unit: String) { self.unit = unit }
    weak var tenant: Person?
    deinit { print("Apartment \(unit) is being deinitialized") }
}

var john: Person?
var unit4A: Apartment?

john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")

john!.apartment = unit4A
unit4A!.tenant = john

john = nil
// 打印“John Appleseed is being deinitialized”

内存安全

默认状况下,Swift 会阻止你代码里不安全的行为。

理解内存访问冲突,In-Out 参数的访问冲突,方法里 self 的访问冲突,属性的访问冲突

示例:

func balance(_ x: inout Int, _ y: inout Int) {
    let sum = x + y
    x = sum / 2
    y = sum - x
}
var playerOneScore = 42
var playerTwoScore = 30
balance(&playerOneScore, &playerTwoScore)  // 正常
balance(&playerOneScore, &playerOneScore)
// 错误:playerOneScore 访问冲突

访问控制

访问控制能够限定其它源文件或模块对你的代码的访问。

  • open 和 public 级别可让实体被同一模块源文件中的全部实体访问,在模块外也能够经过导入该模块来访问源文件里的全部实体。一般状况下,你会使用 open 或 public 级别来指定框架的外部接口。

  • internal 级别让实体被同一模块源文件中的任何实体访问,可是不能被模块外的实体访问。一般状况下,若是某个接口只在应用程序或框架内部使用,就能够将其设置为 internal 级别。

  • fileprivate 限制实体只能在其定义的文件内部访问。若是功能的部分实现细节只须要在文件内使用时,可使用 fileprivate 来将其隐藏。

  • private 限制实体只能在其定义的做用域,以及同一文件内的 extension 访问。若是功能的部分细节只须要在当前做用域内使用时,可使用 private 来将其隐藏。

open 为最高访问级别(限制最少),private 为最低访问级别(限制最多)。
open 只能做用于类和类的成员,它和 public 的区别主要在于 open 限定的类和成员可以在模块外能被继承和重写。
示例:

public class SomePublicClass {}
internal class SomeInternalClass {}
fileprivate class SomeFilePrivateClass {}
private class SomePrivateClass {}

class SomeInternalClass {}   // 隐式 internal
var someInternalConstant = 0 // 隐式 internal

public class SomePublicClass {                  // 显式 public 类
    public var somePublicProperty = 0            // 显式 public 类成员
    var someInternalProperty = 0                 // 隐式 internal 类成员
    fileprivate func someFilePrivateMethod() {}  // 显式 fileprivate 类成员
    private func somePrivateMethod() {}          // 显式 private 类成员
}

class SomeInternalClass {                       // 隐式 internal 类
    var someInternalProperty = 0                 // 隐式 internal 类成员
    fileprivate func someFilePrivateMethod() {}  // 显式 fileprivate 类成员
    private func somePrivateMethod() {}          // 显式 private 类成员
}

fileprivate class SomeFilePrivateClass {        // 显式 fileprivate 类
    func someFilePrivateMethod() {}              // 隐式 fileprivate 类成员
    private func somePrivateMethod() {}          // 显式 private 类成员
}

private class SomePrivateClass {                // 显式 private 类
    func somePrivateMethod() {}                  // 隐式 private 类成员
}

高级运算符

Swift还提供了数种能够对数值进行复杂运算的高级运算符。它们包含了位运算符和移位运算符。

位运算符、溢出运算符、优先级和结合性、运算符函数、自定义运算符

示例:

let initialBits: UInt8 = 0b00001111
let invertedBits = ~initialBits // 等于 0b11110000

var potentialOverflow = Int16.max
// potentialOverflow 的值是 32767,这是 Int16 能容纳的最大整数
potentialOverflow += 1
// 这里会报错

struct Vector2D {
    var x = 0.0, y = 0.0
}

extension Vector2D {
    static func + (left: Vector2D, right: Vector2D) -> Vector2D {
        return Vector2D(x: left.x + right.x, y: left.y + right.y)
    }
}

let vector = Vector2D(x: 3.0, y: 1.0)
let anotherVector = Vector2D(x: 2.0, y: 4.0)
let combinedVector = vector + anotherVector
// combinedVector 是一个新的 Vector2D 实例,值为 (5.0, 5.0)

更多内容

本文分享自微信公众号 - 悟空聊架构(PassJava666)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。