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()
// 創(chuàng)建1個能存放任意類型的數組
// var data = Array<Any>()
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离福、錯誤處理

開發(fā)過程常見的錯誤:語法錯誤(編譯報錯),邏輯錯誤炼蛤,運行時錯誤(可能會導致閃退妖爷,一般也叫做異常)等。

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等)離開代碼塊前必須要執(zhí)行的代碼。defer 語句將延遲至當前作用域結束之前執(zhí)行羽嫡。多個 defer 語句的執(zhí)行順序與定義順序相反本姥。

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<T>(_ 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, T2>(_ t1: T1, _ t2: T2) {}
var fn: (Int, Double) -> () = test

泛型使用

class Stack<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 SubStack<E> : Stack<E> {}
struct Stack<E> {
    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<Int>()
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<T> {
    case point(T)
    case grade(String)
}
let score0 = Score<Int>.point(100)
let score1 = Score.point(99)
let score2 = Score.point(99.5)
let score3 = Score<Int>.grade("A")
1矮燎、關聯類型

關聯類型的作用:給協議中用到的類型定義一個占位名稱。協議中可以擁有多個關聯類型赔癌。

protocol Stackable {
    associatedtype Element // 關聯類型
    mutating func push(_ element: Element)
    mutating func pop() -> Element
    func top() -> Element
    func size() -> Int
}
class Stack<E> : 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<T : Person & Runnable>(_ a: inout T, _ b: inout T) {
    (a, b) = (b, a)
}
protocol Stackable {
    associatedtype Element: Equatable
}
class Stack<E : Equatable> : Stackable { typealias Element = E }
func equal<S1: Stackable, S2: Stackable>(_ s1: S1, _ s2: S2) -> Bool where S1.Element == S2.Element, S1.Element : Hashable {
    return false
}
var stack1 = Stack<Int>()
var stack2 = Stack<String>()
// 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<T : Runnable>(_ 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<Wrapped> : ExpressibleByNilLiteral {
    case none
    case some(Wrapped)
    public init(_ some: Wrapped)
}

4埠忘、String 和 Array

1、1個 String 變量占用多少內存馒索?

16 個字節(jié)

2莹妒、下面2個 String 變量,底層存儲有什么不同绰上?
var str1 = "0123456789"
var str2 = "0123456789ABCDEF"

str1 存儲在 str1 的內存中旨怠,其中最高位用來標識字符串長度,其余位用來存儲數據蜈块。
str2 存儲在內存的常量區(qū)鉴腻,str2 的前 8 個字節(jié)的最低位用來標識字符串的長度,后 8 個字節(jié)用來標識字符串存儲的地址百揭。

字符串的存儲:先比較字符串的長度爽哎,如果字符串的長度小于 16,則字符串的存儲類似 OC 中的 targetpoint 方式器一,直接存儲在字符串的內存中课锌;否則字符串存儲在常量區(qū)。

3、如果對 String 進行拼接操作渺贤, String 變量的存儲會發(fā)生什么變化雏胃?

1、若改變前字符串長度小于 16志鞍,改變后字符串長度扔小于 16瞭亮,則存儲在字符串的內存中。
2固棚、過改變前字符串長度小于 16统翩,改變后字符串長度大于 16,則開辟堆空間玻孟,存儲在堆區(qū)唆缴。
3、若改變前字符串長度大于 16黍翎,改變后字符串長度扔大于 16面徽,則開辟堆空間,存儲在堆區(qū)匣掸。因為改變前字符串長度大于 16 是存儲在常量區(qū)趟紊,常量區(qū)不能更改,故改變后的字符串存儲在堆區(qū)碰酝。
4霎匈、若改變前字符串長度大于 16,改變后字符串長度小于于 16送爸,則存儲在字符串的內存中铛嘱。

4、1個 Array 變量占用多少內存袭厂?

8 個字節(jié)

5墨吓、數組中的數據存放在哪里?

堆空間纹磺。
array 的 8 個字節(jié)里面存放的是堆空間的地址值帖烘。其第一個 8 字節(jié)存放著數組相關引用類型信息內存地址,第二個 8 字節(jié)存放引用計數橄杨,第三個 8 字節(jié)存放 array 元素數量秘症,第四個 8 字節(jié)存放數組容量,后面存放數組內容式矫。

注:
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 中綴運算符 : 優(yōu)先級組
precedencegroup 優(yōu)先級組 {
    associativity: 結合性(left\right\none)
    higherThan: 比誰的優(yōu)先級高
    lowerThan: 比誰的優(yōu)先級低
    assignment: true代表在可選鏈操作中擁有跟賦值運算符一樣的優(yōu)先級
}
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..<endIndex).contains(idx) {
            return self[idx]
        }
        return nil
    }
}
3由境、方法
extension Int {
    func repetitions(task: () -> Void) {
        for _ in 0..<self { task() }
    }
    mutating func square() -> Int {
        self = self * self
        return self
    }
}
4棚亩、嵌套
extension Int {
    func repetitions(task: () -> Void) {
        for _ in 0..<self { task() }
    }
    mutating func square() -> 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..<digitIndex { decimalBase *= 10 }
        return (self / decimalBase) % 10
    }
}
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<T: BinaryInteger>(_ 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<E> {
    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慷嗜、不能添加指定初始化器淀弹,不能添加反初始化器。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末庆械,一起剝皮案震驚了整個濱河市薇溃,隨后出現的幾起案子,更是在濱河造成了極大的恐慌缭乘,老刑警劉巖痊焊,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異忿峻,居然都是意外死亡薄啥,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進店門逛尚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來垄惧,“玉大人,你說我怎么就攤上這事绰寞〉窖罚” “怎么了?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵滤钱,是天一觀的道長觉壶。 經常有香客問我,道長件缸,這世上最難降的妖魔是什么铜靶? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮他炊,結果婚禮上争剿,老公的妹妹穿的比我還像新娘。我一直安慰自己痊末,他們只是感情好蚕苇,可當我...
    茶點故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著凿叠,像睡著了一般涩笤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上盒件,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天蹬碧,我揣著相機與錄音,去河邊找鬼履恩。 笑死锰茉,一個胖子當著我的面吹牛呢蔫,可吹牛的內容都是我干的切心。 我是一名探鬼主播飒筑,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼绽昏!你這毒婦竟也來了协屡?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤全谤,失蹤者是張志新(化名)和其女友劉穎肤晓,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體认然,經...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡补憾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了卷员。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片盈匾。...
    茶點故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖毕骡,靈堂內的尸體忽然破棺而出削饵,到底是詐尸還是另有隱情,我是刑警寧澤未巫,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布窿撬,位于F島的核電站,受9級特大地震影響叙凡,放射性物質發(fā)生泄漏劈伴。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一握爷、第九天 我趴在偏房一處隱蔽的房頂上張望宰啦。 院中可真熱鬧,春花似錦饼拍、人聲如沸赡模。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽漓柑。三九已至,卻和暖如春叨吮,著一層夾襖步出監(jiān)牢的瞬間辆布,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工茶鉴, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留锋玲,地道東北人。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓涵叮,卻偏偏與公主長得像惭蹂,于是被迫代替她去往敵國和親伞插。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,914評論 2 355

推薦閱讀更多精彩內容