Swift 之协议、错误类型、泛型、String 和 Array、高级运算符、扩展

1、协议

1、协议可以用来定义方法、属性、下标的声明,协议可以被枚举、结构体、类遵守(多个协议之间用逗号隔开)。

protocol Drawable {
    func draw()
    var x: Int { get set }
    var y: Int { get }
    subscript(index: Int) -> Int { get set }
}
protocol Test1 {}
protocol Test2 {}
protocol Test3 {}
class TestClass : Test1, Test2, Test3 {}

2、协议中定义方法时不能有默认参数值。默认情况下,协议中定义的内容必须全部都实现。

1、属性

协议中定义属性时必须用 var 关键字。实现协议时的属性权限要不小于协议中定义的属性权限:协议定义 get、set,用 var 存储属性或 get、set 计算属性去实现;协议定义 get,用任何属性都可以实现。

protocol Drawable {
    func draw()
    var x: Int { get set }
    var y: Int { get }
    subscript(index: Int) -> Int { get set }
}
class Person : Drawable {
    var x: Int = 0
    let y: Int = 0
    func draw() {
        print("Person draw")
    }
    subscript(index: Int) -> Int {
        set {}
        get { index }
    }
}
class Person : Drawable {
    var x: Int {
        get { 0 }
        set {}
    }
    var y: Int { 0 }
    func draw() { print("Person draw") }
    subscript(index: Int) -> Int {
        set {}
        get { index }
    }
}
2、static、class

为了保证通用,协议中必须用 static 定义类型方法、类型属性、类型下标。这是因为用 class 的话只能被类使用,不能被值类型使用。

protocol Drawable {
    static func draw()
}
class Person1 : Drawable {
    class func draw() {
        print("Person1 draw")
    }
}
class Person2 : Drawable {
    static func draw() {
        print("Person2 draw")
    }
}
3、mutating

只有将协议中的实例方法标记为 mutating,才允许结构体、枚举的具体实现修改自身内存。在实现方法时不用加 mutating,枚举结构体才需要加 mutating 。

protocol Drawable {
    mutating func draw()
}
class Size : Drawable {
    var width: Int = 0
    func draw() {
        width = 10
    }
}
struct Point : Drawable {
    var x: Int = 0
    mutating func draw() {
        x = 10
    }
}
4、init、init?、init!

1、协议中还可以定义初始化器 init,非 final 类实现时必须加上 required。

protocol Drawable {
    init(x: Int, y: Int)
}
class Point : Drawable {
    required init(x: Int, y: Int) {}
}
final class Size : Drawable {
    init(x: Int, y: Int) {}
}

2、如果从协议实现的初始化器,刚好是重写了父类的指定初始化器,那么这个初始化必须同时加 required、override。

protocol Livable {
    init(age: Int)
}
class Person {
    init(age: Int) {}
}

class Student : Person, Livable {
    required override init(age: Int) {
        super.init(age: age)
    }
}

//也可以前面加 final 不用写 required
final class Student: Person, Livable {
    override init(age: Int) {
        super.init(age: age)
    }
}

3、协议中定义的 init?、init!,可以用 init、init?、init! 去实现。协议中定义的 init,可以用 init、init! 去实现。

protocol Livable {
    init()
    init?(age: Int)
    init!(no: Int)
}
class Person : Livable {
    required init() {}
    // required init!() {}
    required init?(age: Int) {}
    // required init!(age: Int) {}
    // required init(age: Int) {}
    required init!(no: Int) {}
    // required init?(no: Int) {}
    // required init(no: Int) {}
}
5、继承

一个协议可以继承其他协议。

protocol Runnable {
    func run()
}
protocol Livable : Runnable {
    func breath()
}
class Person : Livable {
    func breath() {}
    func run() {}
}
6、组合

协议组合,最多可以包含1个类类型

protocol Livable {}
protocol Runnable {}
class Person {}

// 接收Person或者其子类的实例
func fn0(obj: Person) {}
// 接收遵守Livable协议的实例
func fn1(obj: Livable) {}
// 接收同时遵守Livable、Runnable协议的实例
func fn2(obj: Livable & Runnable) {}
// 接收同时遵守Livable、Runnable协议、并且是Person或者其子类的实例
func fn3(obj: Person & Livable & Runnable) {}

typealias RealPerson = Person & Livable & Runnable
// 接收同时遵守Livable、Runnable协议、并且是Person或者其子类的实例
func fn4(obj: RealPerson) {}
7、CaseIterable

枚举遵守 CaseIterable 协议,可以实现遍历枚举值。

enum Season : CaseIterable {
    case spring, summer, autumn, winter
}
let seasons = Season.allCases
print(seasons.count) // 4
for season in seasons {
    print(season)
} // spring summer autumn winter
8、CustomStringConvertible

遵守 CustomStringConvertible、 CustomDebugStringConvertible 协议,都可以自定义实例的打印字符串。

class Person : CustomStringConvertible, CustomDebugStringConvertible {
    var age = 0
    var description: String { "person_\(age)" }
    var debugDescription: String { "debug_person_\(age)" }
}
var person = Person()
print(person) // person_0
debugPrint(person) // debug_person_0
9、Any、AnyObject

Swift 提供了2种特殊的类型:Any、AnyObject。
Any:可以代表任意类型(枚举、结构体、类,也包括函数类型)。
AnyObject:可以代表任意类类型。在协议后面写上 AnyObject 代表只有类能遵守这个协议,在协议后面写上 class 也代表只有类能遵守这个协议。

var stu: Any = 10
stu = "Jack"
stu = Student()
// 创建1个能存放任意类型的数组
// var data = Array()
var data = [Any]()
data.append(1)
data.append(3.14)
data.append(Student())
data.append("Jack")
data.append({ 10 })
10、is、as?、as!、as

is 用来判断是否为某种类型,as 用来做强制类型转换。

protocol Runnable { func run() }
class Person {}

class Student : Person, Runnable {
    func run() {
        print("Student run")
    }
    func study() {
        print("Student study")
    }
}
var stu: Any = 10
print(stu is Int) // true
stu = "Jack"
print(stu is String) // true
stu = Student()
print(stu is Person) // true
print(stu is Student) // true
print(stu is Runnable) // true
var stu: Any = 10
(stu as? Student)?.study() // 没有调用study
stu = Student()
(stu as? Student)?.study() // Student study
(stu as! Student).study() // Student study
(stu as? Runnable)?.run() // Student run
11、X.self、X.Type、AnyClass

X.self 是一个元类型(metadata)的指针,metadata 存放着类型相关信息。X.self 属于 X.Type 类型。

class Person {}
class Student : Person {}
var perType: Person.Type = Person.self
var stuType: Student.Type = Student.self
perType = Student.self
var anyType: AnyObject.Type = Person.self
anyType = Student.self
public typealias AnyClass = AnyObject.Type
var anyType2: AnyClass = Person.self
anyType2 = Student.self
var per = Person()
var perType = type(of: per) // Person.self
print(Person.self == type(of: per)) // true
12、元类型
class Animal { required init() {} }
class Cat : Animal {}
class Dog : Animal {}
class Pig : Animal {}
func create(_ clses: [Animal.Type]) -> [Animal] {
    var arr = [Animal]()
    for cls in clses {
        arr.append(cls.init())
    }
    return arr
}
print(create([Cat.self, Dog.self, Pig.self]))
import Foundation
class Person {
    var age: Int = 0
}
class Student : Person {
    var no: Int = 0
}
print(class_getInstanceSize(Student.self)) // 32
print(class_getSuperclass(Student.self)!) // Person
print(class_getSuperclass(Person.self)!) // Swift._SwiftObject
13、Self

1、Self 代表当前类型。

class Person {
    var age = 1
    static var count = 2
    func run() {
        print(self.age) // 1
        print(Self.count) // 2
    }
}

2、Self 一般用作返回值类型,限定返回值跟方法调用者必须是同一类型(也可以作为参数类型)。

protocol Runnable {
    func test() -> Self
}
class Person : Runnable {
    required init() {}
    func test() -> Self { type(of: self).init() }
}
class Student : Person {}


var p = Person()
print(p.test())// Person

var stu = Student()
print(stu.test())// Student

2、错误处理

开发过程常见的错误:语法错误(编译报错),逻辑错误,运行时错误(可能会导致闪退,一般也叫做异常)等。

1、自定义错误

1、Swift 中可以通过 Error 协议自定义运行时的错误信息。

enum SomeError : Error {
    case illegalArg(String)
    case outOfBounds(Int, Int)
    case outOfMemory
}

2、函数内部通过 throw 抛出自定义 Error,可能会抛出 Error 的函数必须加上 throws 声明。

func divide(_ num1: Int, _ num2: Int) throws -> Int {
    if num2 == 0 {
        throw SomeError.illegalArg("0不能作为除数")
    }
    return num1 / num2
}

3、需要使用 try 调用可能会抛出 Error 的函数。

var result = try divide(20, 10)
2、do-catch

可以使用 do-catch 捕捉 Error。抛出 Error 后,try 下一句直到作用域结束的代码都将停止运行。

func test() {
    print("1")
    do {
        print("2")
        print(try divide(20, 0))
        print("3")
    } catch let SomeError.illegalArg(msg) {
        print("参数异常:", msg)
    } catch let SomeError.outOfBounds(size, index) {
        print("下标越界:", "size=\(size)", "index=\(index)")
    } catch SomeError.outOfMemory {
        print("内存溢出")
    } catch {
        print("其他错误")
    }
    print("4")
}

test()
// 1
// 2
// 参数异常: 0不能作为除数
// 4
do {
    try divide(20, 0)
} catch let error {
    switch error {
    case let SomeError.illegalArg(msg):
        print("参数错误:", msg)
    default:
        print("其他错误")
    }
}
3、处理 Error

处理 Error 的 2 种方式:
1、通过 do-catch 捕捉 Error。
2、不捕捉 Error,在当前函数增加 throws 声明,Error 将自动抛给上层函数。依次类推,如果最顶层函数(main函数)依然没有捕捉 Error,那么程序将终止。

do {
    print(try divide(20, 0))
} catch is SomeError {
    print("SomeError")
}
func test() throws {
    print("1")
    print(try divide(20, 0))
    print("2")
}
try test()
// 1
// Fatal error: Error raised at top level
func test() throws {
    print("1")
    do {
        print("2")
        print(try divide(20, 0))
        print("3")
    } catch let error as SomeError {
        print(error)
    }
        print("4")
}
try test()
// 1
// 2
// illegalArg("0不能作为除数")
// 4
4、try?、try!

可以使用 try?、try! 调用可能会抛出 Error 的函数,这样就不用去处理 Error。

func test() {
    print("1")
    var result1 = try? divide(20, 10) // Optional(2), Int?
    var result2 = try? divide(20, 0) // nil
    var result3 = try! divide(20, 10) // 2, Int
    print("2")
}
test()
var a = try? divide(20, 0)
var b: Int?
do {
    b = try divide(20, 0)
} catch { b = nil }
5、rethrows

rethrows 表明:函数本身不会抛出错误,但调用闭包参数抛出错误,那么它会将错误向上抛。

func exec(_ fn: (Int, Int) throws -> Int, _ num1: Int, _ num2: Int) rethrows {
    print(try fn(num1, num2))
}
try exec(divide, 20, 0)// Fatal error: Error raised at top level
6、defer

defer 语句:用来定义以任何方式(抛错误、return等)离开代码块前必须要执行的代码。defer 语句将延迟至当前作用域结束之前执行。多个 defer 语句的执行顺序与定义顺序相反。

func open(_ filename: String) -> Int {
    print("open")
    return 0
}
func close(_ file: Int) {
    print("close")
}

func processFile(_ filename: String) throws {
    let file = open(filename)
    defer {
        close(file)
    }
    // 使用file
    // ....
    try divide(20, 0)
    // close将会在这里调用
}
try processFile("test.txt")// open -- > close --> Fatal error: Error raised at top level
7、assert(断言)

不符合指定条件就抛出运行时错误,常用于调试(Debug)阶段的条件判断。默认情况下,Swift 的断言只会在 Debug 模式下生效,Release 模式下会忽略。

func divide(_ v1: Int, _ v2: Int) -> Int {
    assert(v2 != 0, "除数不能为0")
    return v1 / v2
}
print(divide(20, 0))
8、fatalError

1、如果遇到严重问题,希望结束程序运行时,可以直接使用 fatalError 函数抛出错误(这是无法通过 do-catch 捕捉的错误)。使用了 fatalError 函数,就不需要再写 return。

func test(_ num: Int) -> Int {
    if num >= 0 {
        return 1
    }
    fatalError("num不能小于0")
}

2、在某些不得不实现、但不希望别人调用的方法,可以考虑内部使用 fatalError 函数。

class Person { required init() {} }
class Student : Person {
    required init() { fatalError("don't call Student.init") }
    init(score: Int) {}
}
var stu1 = Student(score: 98)
var stu2 = Student()
9、局部作用域

可以使用 do 实现局部作用域。

do {
    let dog1 = Dog()
    dog1.age = 10
    dog1.run()
}
do {
    let dog2 = Dog()
    dog2.age = 10
    dog2.run()
}

3、泛型

泛型可以将类型参数化,提高代码复用率,减少代码量。在使用的时候再确定具体类型。泛型可以使用在类、结构体、枚举。

func swapValues(_ a: inout T, _ b: inout T) {
    (a, b) = (b, a)
}
var i1 = 10
var i2 = 20
swapValues(&i1, &i2)

var d1 = 10.0
var d2 = 20.0
swapValues(&d1, &d2)
struct Date {
    var year = 0, month = 0, day = 0
}
var dd1 = Date(year: 2011, month: 9, day: 10)
var dd2 = Date(year: 2012, month: 10, day: 11)
swapValues(&dd1, &dd2)
func test(_ t1: T1, _ t2: T2) {}
var fn: (Int, Double) -> () = test

泛型使用

class Stack {
    var elements = [E]()
    func push(_ element: E) { elements.append(element) }
    func pop() -> E { elements.removeLast() }
    func top() -> E { elements.last! }
    func size() -> Int { elements.count }
}
class SubStack : Stack {}
struct Stack {
    var elements = [E]()
    mutating func push(_ element: E) { elements.append(element) }
    mutating func pop() -> E { elements.removeLast() }
    func top() -> E { elements.last! }
    func size() -> Int { elements.count }
}
var stack = Stack()
stack.push(11)
stack.push(22)
stack.push(33)
print(stack.top()) // 33
print(stack.pop()) // 33
print(stack.pop()) // 22
print(stack.pop()) // 11
print(stack.size()) // 0
enum Score {
    case point(T)
    case grade(String)
}
let score0 = Score.point(100)
let score1 = Score.point(99)
let score2 = Score.point(99.5)
let score3 = Score.grade("A")
1、关联类型

关联类型的作用:给协议中用到的类型定义一个占位名称。协议中可以拥有多个关联类型。

protocol Stackable {
    associatedtype Element // 关联类型
    mutating func push(_ element: Element)
    mutating func pop() -> Element
    func top() -> Element
    func size() -> Int
}
class Stack : Stackable {
    // typealias Element = E
    var elements = [E]()
    func push(_ element: E) { 
        elements.append(element) 
    }
    func pop() -> E { elements.removeLast() }
    func top() -> E { elements.last! }
    func size() -> Int { elements.count }
}
class StringStack : Stackable {
    // 给关联类型设定真实类型
    // typealias Element = String
    var elements = [String]()
    func push(_ element: String) { elements.append(element) }
    func pop() -> String { elements.removeLast() }
    func top() -> String { elements.last! }
    func size() -> Int { elements.count }
}
var ss = StringStack()
ss.push("Jack")
ss.push("Rose")
2、类型约束
protocol Runnable { }
class Person { }
func swapValues(_ a: inout T, _ b: inout T) {
    (a, b) = (b, a)
}
protocol Stackable {
    associatedtype Element: Equatable
}
class Stack : Stackable { typealias Element = E }
func equal(_ s1: S1, _ s2: S2) -> Bool where S1.Element == S2.Element, S1.Element : Hashable {
    return false
}
var stack1 = Stack()
var stack2 = Stack()
// error: requires the types 'Int' and 'String' be equivalent
equal(stack1, stack2)
3、协议中使用 associatedtype 报错问题
protocol Runnable {
    associatedtype Speed
    var speed: Speed { get }
}
class Person: Runnable {
    var speed: Double { 0.0 }
}
class Car: Runnable {
    var speed: Int { 0 }
}

解决方案一:使用泛型

func get(_ type: Int) -> T {
    if type == 0 {
        return Person() as! T
    }
    return Car() as! T
}
var r1: Person = get(0)
var r2: Car = get(1)

解决方案二:使用 some 关键字声明一个不透明类型。some 限制只能返回一种类型。

func get(_ type: Int) -> some Runnable { Car() }
var r1 = get(0)
var r2 = get(1)
4、some

some 除了用在返回值类型上,一般还可以用在属性类型上。

protocol Runnable { associatedtype Speed }
class Dog : Runnable { typealias Speed = Double }
class Person {
    var pet: some Runnable {
        return Dog()
    }
}
5、可选项的本质

可选项的本质是 enum 类型,要么为 none,要么 some。

public enum Optional : ExpressibleByNilLiteral {
    case none
    case some(Wrapped)
    public init(_ some: Wrapped)
}

4、String 和 Array

1、1个 String 变量占用多少内存?

16 个字节

2、下面2个 String 变量,底层存储有什么不同?
var str1 = "0123456789"
var str2 = "0123456789ABCDEF"

str1 存储在 str1 的内存中,其中最高位用来标识字符串长度,其余位用来存储数据。
str2 存储在内存的常量区,str2 的前 8 个字节的最低位用来标识字符串的长度,后 8 个字节用来标识字符串存储的地址。

字符串的存储:先比较字符串的长度,如果字符串的长度小于 16,则字符串的存储类似 OC 中的 targetpoint 方式,直接存储在字符串的内存中;否则字符串存储在常量区。

3、如果对 String 进行拼接操作, String 变量的存储会发生什么变化?

1、若改变前字符串长度小于 16,改变后字符串长度扔小于 16,则存储在字符串的内存中。
2、过改变前字符串长度小于 16,改变后字符串长度大于 16,则开辟堆空间,存储在堆区。
3、若改变前字符串长度大于 16,改变后字符串长度扔大于 16,则开辟堆空间,存储在堆区。因为改变前字符串长度大于 16 是存储在常量区,常量区不能更改,故改变后的字符串存储在堆区。
4、若改变前字符串长度大于 16,改变后字符串长度小于于 16,则存储在字符串的内存中。

4、1个 Array 变量占用多少内存?

8 个字节

5、数组中的数据存放在哪里?

堆空间。
array 的 8 个字节里面存放的是堆空间的地址值。其第一个 8 字节存放着数组相关引用类型信息内存地址,第二个 8 字节存放引用计数,第三个 8 字节存放 array 元素数量,第四个 8 字节存放数组容量,后面存放数组内容。

注:
1、数组的容量会自动扩容至元素个数的两倍,且是8的倍数。
2、数组的表象是结构体,但其本质是引用类型。

5、高级运算符

1、溢出运算符

Swift 的算数运算符出现溢出时会抛出运行时错误。Swift 有溢出运算符(&+、&-、&*),用来支持溢出运算。

var min = UInt8.min
print(min &- 1) // 255, Int8.max
var max = UInt8.max
print(max &+ 1) // 0, Int8.min
print(max &* 2) // 254, 等价于 max &+ max
2、运算符重载

类、结构体、枚举可以为现有的运算符提供自定义的实现,这个操作叫做:运算符重载。

struct Point {
    var x: Int, y: Int
}
func + (p1: Point, p2: Point) -> Point {
    Point(x: p1.x + p2.x, y: p1.y + p2.y)
}
let p = Point(x: 10, y: 20) + Point(x: 11, y: 22)
print(p) // Point(x: 21, y: 42)
struct Point {
    var x: Int, y: Int
    static func + (p1: Point, p2: Point) -> Point {
        Point(x: p1.x + p2.x, y: p1.y + p2.y)
    }
}
static func + (p1: Point, p2: Point) -> Point {
    Point(x: p1.x + p2.x, y: p1.y + p2.y)
}
static func - (p1: Point, p2: Point) -> Point {
    Point(x: p1.x - p2.x, y: p1.y - p2.y)
}
static prefix func - (p: Point) -> Point {
    Point(x: -p.x, y: -p.y)
}
static func += (p1: inout Point, p2: Point) {
    p1 = p1 + p2
}
static prefix func ++ (p: inout Point) -> Point {
    p += Point(x: 1, y: 1)
    return p
}
static postfix func ++ (p: inout Point) -> Point {
    let tmp = p
    p += Point(x: 1, y: 1)
    return tmp
}
static func == (p1: Point, p2: Point) -> Bool {
    (p1.x == p2.x) && (p1.y == p2.y)
}
3、Equatable

1、要想得知2个实例是否等价,一般做法是遵守 Equatable 协议,重载 == 运算符,与此同时,等价于重载了 != 运算符。

struct Point : Equatable {
    var x: Int, y: Int
}
var p1 = Point(x: 10, y: 20)
var p2 = Point(x: 11, y: 22)
print(p1 == p2) // false
print(p1 != p2) // true

2、Swift 为以下类型提供默认的 Equatable 实现:
没有关联类型的枚举。
只拥有遵守 Equatable 协议关联类型的枚举。
只拥有遵守 Equatable 协议存储属性的结构体。

3、引用类型比较存储的地址值是否相等(是否引用着同一个对象),使用恒等运算符 === 、!==。

4、Comparable

要想比较2个实例的大小,一般做法是:遵守 Comparable 协议,重载相应的运算符。

// score大的比较大,若score相等,age小的比较大
struct Student : Comparable {
    var age: Int
    var score: Int
    init(score: Int, age: Int) {
        self.score = score
        self.age = age
    }
    static func < (lhs: Student, rhs: Student) -> Bool {
        (lhs.score < rhs.score)
            || (lhs.score == rhs.score && lhs.age > rhs.age)
    }
    static func > (lhs: Student, rhs: Student) -> Bool {
        (lhs.score > rhs.score)
            || (lhs.score == rhs.score && lhs.age < rhs.age)
    }
    static func <= (lhs: Student, rhs: Student) -> Bool {
        !(lhs > rhs)
    }
    static func >= (lhs: Student, rhs: Student) -> Bool {
        !(lhs < rhs)
    }
}
var stu1 = Student(score: 100, age: 20)
var stu2 = Student(score: 98, age: 18)
var stu3 = Student(score: 100, age: 20)
print(stu1 > stu2) // true
print(stu1 >= stu2) // true
print(stu1 >= stu3) // true
print(stu1 <= stu3) // true
print(stu2 < stu1) // true
print(stu2 <= stu1) // true
5、自定义运算符

可以自定义新的运算符:在全局作用域使用 operator 进行声明。

prefix operator 前缀运算符
postfix operator 后缀运算符
infix operator 中缀运算符 : 优先级组
precedencegroup 优先级组 {
    associativity: 结合性(left\right\none)
    higherThan: 比谁的优先级高
    lowerThan: 比谁的优先级低
    assignment: true代表在可选链操作中拥有跟赋值运算符一样的优先级
}
prefix operator +++
infix operator +- : PlusMinusPrecedence
precedencegroup PlusMinusPrecedence {
    associativity: none
    higherThan: AdditionPrecedence
    lowerThan: MultiplicationPrecedence
    assignment: true
}
struct Point {
    var x: Int, y: Int
    static prefix func +++ (point: inout Point) -> Point {
        point = Point(x: point.x + point.x, y: point.y + point.y)
        return point
    }
    static func +- (left: Point, right: Point) -> Point {
        return Point(x: left.x + right.x, y: left.y - right.y)
    }
    static func +- (left: Point?, right: Point) -> Point {
        print("+-")
        return Point(x: left?.x ?? 0 + right.x, y: left?.y ?? 0 - right.y)
    }
}
struct Person {
    var point: Point
}
var person: Person? = nil
person?.point +- Point(x: 10, y: 20)

6、扩展

Swift 中的扩展,有点类似于 OC 中的分类(Category)。扩展可以为枚举、结构体、类、协议添加新功能,可以添加方法、计算属性、下标、(便捷)初始化器、嵌套类型、协议等等。

1、计算属性
extension Double {
    var km: Double { self * 1_000.0 }
    var m: Double { self }
    var dm: Double { self / 10.0 }
    var cm: Double { self / 100.0 }
    var mm: Double { self / 1_000.0 }
}
2、下标
extension Array {
    subscript(nullable idx: Int) -> Element? {
        if (startIndex..
3、方法
extension Int {
    func repetitions(task: () -> Void) {
        for _ in 0.. Int {
        self = self * self
        return self
    }
}
4、嵌套
extension Int {
    func repetitions(task: () -> Void) {
        for _ in 0.. Int {
        self = self * self
        return self
    }
    enum Kind { case negative, zero, positive }
    var kind: Kind {
        switch self {
        case 0: return .zero
        case let x where x > 0: return .positive
        default: return .negative
        }
    }
    subscript(digitIndex: Int) -> Int {
        var decimalBase = 1
        for _ in 0..
5、初始化器
struct Point {
    var x: Int = 0
    var y: Int = 0
}
extension Point {
    init(_ point: Point) {
        self.init(x: point.x, y: point.y)
    }
}
var p1 = Point()
var p2 = Point(x: 10)
var p3 = Point(y: 20)
var p4 = Point(x: 10, y: 20)
var p5 = Point(p4)

如果希望自定义初始化器的同时,编译器也能够生成默认初始化器,可以在扩展中编写自定义初始化器。required 初始化器也不能写在扩展中。

6、协议
class Person {
    var age: Int
    var name: String
    init(age: Int, name: String) {
        self.age = age
        self.name = name
    }
}
extension Person : Equatable {
    static func == (left: Person, right: Person) -> Bool {
        left.age == right.age && left.name == right.name
    }
    convenience init() {
        self.init(age: 0, name: "")
    }
}

如果一个类型已经实现了协议的所有要求,但是还没有声明它遵守了这个协议,可以通过扩展来让它遵守这个协议。

protocol TestProtocol {
    func test()
}
class TestClass {
    func test() {
        print("test")
    }
}
extension TestClass : TestProtocol {}
//编写一个函数,判断一个整数是否为奇数?
func isOdd(_ i: T) -> Bool {
    i % 2 != 0
}
extension BinaryInteger {
    func isOdd() -> Bool { self % 2 != 0 }
}

扩展可以给协议提供默认实现,也间接实现『可选协议』的效果。扩展可以给协议扩充『协议中从未声明过的方法』。

protocol TestProtocol {
    func test1()
}
extension TestProtocol {
    func test1() {
        print("TestProtocol test1")
    }
    func test2() {
        print("TestProtocol test2")
    }
}
class TestClass : TestProtocol {}
var cls = TestClass()
cls.test1() // TestProtocol test1
cls.test2() // TestProtocol test2
var cls2: TestProtocol = TestClass()
cls2.test1() // TestProtocol test1
cls2.test2() // TestProtocol test2
class TestClass : TestProtocol {
    func test1() { print("TestClass test1") }
    func test2() { print("TestClass test2") }
}
var cls = TestClass()
cls.test1() // TestClass test1
cls.test2() // TestClass test2
var cls2: TestProtocol = TestClass()
cls2.test1() // TestClass test1
cls2.test2() // TestProtocol test2
7、泛型
class Stack {
    var elements = [E]()
    func push(_ element: E) {
        elements.append(element)
    }
    func pop() -> E { elements.removeLast() }
    func size() -> Int { elements.count }
}
// 扩展中依然可以使用原类型中的泛型类型
extension Stack {
    func top() -> E { elements.last! }
}
// 符合条件才扩展
extension Stack : Equatable where E : Equatable {
    static func == (left: Stack, right: Stack) -> Bool {
        left.elements == right.elements
    }
}
8、扩展不能办到的事情

1、不能覆盖原有的功能。
2、不能添加存储属性,不能向已有的属性添加属性观察器。
3、不能添加父类。
4、不能添加指定初始化器,不能添加反初始化器。

你可能感兴趣的:(Swift 之协议、错误类型、泛型、String 和 Array、高级运算符、扩展)