Swift 5.1 温故而知新笔记系列之第四天

1.协议

  • 协议可以用来定义方法,属性,下标的声明,协议可以被枚举,结构体,类遵守。
  • 协议中定义属性时必须用var关键字
  • 实现协议时的属性权限要不小于协议中定义的属性权限
    • 协议定义getset,用var存储属性或者getset计算属性去实现
    • 协议定义get,用任何属性都可以实现
  • 为了保证通用,协议中必须用static定义类型方法,类型属性,类型下标(class就只能用哪于类,因此用static更通用)
  • 只有将协议的实例方法标记为mutating才允许结构体,枚举的具体实现修改自身内存,类的方法实现时不用加mutating,枚举、结构体才要加
  • 协议中还可以定义初始化器init,非final类实现时必须加上required,可以保证子类继承也是遵守同一个初始化器的
protocol Drawable {
    static func circle()
    mutating func draw()
    init(x: Int, y: Int)
    var x: Int { get set }
    var y: Int { get }
    subscript(index: Int) -> Int { get set }
}



class Pencil : Drawable{
    
    static func circle() {
        print("Pencil Circle 静态方法")
    }
    
    required init(x: Int, y: Int) {
        self.x = x
        self.y = y
    }
    
    func draw() {
        x = 99
        print("Pencil Draw")
    }
    
    var x: Int = 100
    
    var y: Int = 200
    
    subscript(index: Int) -> Int {
        set {x = newValue}
        get { index }
    }
}

var p1 = Pencil(x: 100, y: 200)
p1.draw()
Pencil.circle()
print(p1[100])


struct Brush : Drawable {
    static func circle() {
        print("Brush Circle 静态方法")
    }
    
    mutating func draw() {
        x = 88
        print("Brush Draw")
    }
    
    
    var x: Int = 10
    
    var y: Int = 20
    
    subscript(index: Int) -> Int {
        set {x = newValue}
        get { index }
    }
}
  • 如果从协议实现的初始化器,同时刚好重写了父类的指定初始化器,那么这个初始化器必须同时加requiredoverride
protocol Livable {
    init(age: Int)
}

class People {
    init(age: Int) {}
}


class Student : People, Livable {
    required override init(age: Int) {
        super.init(age: age)
    }
}
  • 协议中定义了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) {}
}

2.Any、 AnyObject

  • Any: 可以代表任意类型(枚举,结构体,类,函数类型)
  • AnyObject: 可以代表任意类型,写在协议后面,AnyObject代表只有类能遵守这个协议
var stu: Any = 10
stu = "Jack"
stu = Student()


// var data = Array<Any>()
var data = [Any]()
data.append(1)
data.append(Student())
data.append("Jack")
data.append({10})

3.is、as?、as!、as

  • is用来判断是否为某种类型
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 = Student()

print(stu is Student) // true
print(stu is Person)  // true
print(stu is Runnable)// true

// 类似OC里面的isKindOf
  • 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

(stu as? Student)?.study() // 不会调用


stu = Student()

(stu as? Student)?.study() // Student study
(stu as! Student).study() // Student study
(stu as? Runnable)?.run() // Student Run


var data = Array<Any>()

data.append(Int("123") as Any)

print(data)

data.append(10 as Any)
print(data)
// [Optional(123)]
// [Optional(123), 10]

4.X.self、X.Type、AnyClass

  • X.self是一个元类型(metadata)的指针,存放着类型相关信息
  • X.selfX.Type类型
var p: Person = Person()
var pType: Person.Type = Person.self

PersonType类型是什么东西?
在这里插入图片描述
查看汇编右侧有基本的提示

0x100001988 <+24>: callq  0x1000019c0               ; type metadata accessor for Swift02.Person at <compiler-generated>
0x10000198d <+29>: movq   %rax, %r13
0x100001990 <+32>: movq   %rax, -0x20(%rbp)

该函数调用返回值写入rax,然后再写入-0x20(%rbp)保存

(lldb) register read rax
     rax = 0x0000000100002180  type metadata for Swift02.Person

接着看到Person的初始化赋值给全局变量p

0x100001998 <+40>: callq  0x100001a10               ; Swift02.Person.__allocating_init() -> Swift02.Person at main.swift:156
    0x10000199d <+45>: xorl   %r8d, %r8d
    0x1000019a0 <+48>: movq   %rax, 0x8f9(%rip)         ; Swift02.p : Swift02.Person
    0x1000019a7 <+55>: movq   -0x20(%rbp), %rax

可以通过register的方式

(lldb) register read rax
     rax = 0x000000010061cb50

或者0x8f9 + 0x1000019a7 = 0x1000022A0,通过View Memory的方式查看前八个字节,也是0x000000010061cb50,打印出来查看

(lldb) x/4xg 0x000000010061cb50
0x10061cb50: 0x0000000100002180 0x0000000000000002
0x10061cb60: 0x55706f50534e5b2d 0x206e6f7474754270

最后的汇编是

0x1000019a7 <+55>: movq   -0x20(%rbp), %rax
    0x1000019ab <+59>: movq   %rax, 0x8f6(%rip)         ; Swift02.pType : Swift02.Person.Type

最终还是把存起来的-0x20(%rbp)赋值给rax,然后赋值给全局变量pType,可以看到存储的地址值就是0x0000000100002180,所以X.Type也是指针类型,存储着类对象前八个字节,metadata的信息

X.self类似于 NSObject.class

4.1继承相关
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

按指针和继承理解,父类指针一样可以指向子类对象

type(of:)等价于oc的isMemberOf

class Person {}
class Student : Person {}

var per = Person()
var stu = Student()
var perType = type(of: per) // Person.self
print(Person.self == type(of: per)) // true
print(Person.self == type(of: stu)) // false

通过汇编查看
在这里插入图片描述
var perType = type(of: per)这句代码对应的就是红色框框圈起来的汇编
rdx赋值上去,一步步上上推,就是

movq   0xf3b(%rip), %rax         ; Swift02.per : Swift02.Person
0x1000014e5 <+133>: leaq   -0x20(%rbp), %rdi

意思是把 0xf3b + 0x1000014e5 = 0x100002420地址存储的前八个字节,通过ViewMemory查看

B0 32 58 00 01 00 00 00
// 0x1005832B0 给到rax

赋值给rax寄存器,然后赋值给-0x88(%rb),最后给rcx = 0x1005832B0,继续看下去

movq   (%rcx), %rdx
0x10000150a <+170>: movq   %rdx, 0xf1f(%rip)         ; Swift02.perType : Swift02.Person.Type

(%rxc)通过0x1005832B0查看

(lldb) x/xg 0x1005832B0
0x1005832b0: 0x0000000100002270
(lldb) image lookup --address 0x100002270
      Address: Swift02[0x0000000100002270] (Swift02.__DATA.__data + 56)
      Summary: type metadata for Swift02.Person

然后赋值到rdx,在给到全局变量perType的地址中存储

因此可以看到type(of: )其实严格意义上不算函数,或者说被编译器内联展开了,反正看到的现象是直接取的对象全八个字节进行赋值而已。

5.元类型的使用

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]))

这里可以看到元类型Animal的初始化器加了required,这个细节主要用来表明子类都必须用该初始化器来初始化,统一调用。

swift获取父类的信息

class Person {
    var age: Int = 0
}


class Student: Person {
    var no: Int = 0
}

print(class_getInstanceSize(Student.self)) // 32
print(class_getSuperclass(Student.self)!) // Persn=on
print(class_getSuperclass(Person.self)!) // Swift._SwiftObject

6.Self

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

protocol Runable {
    func test() -> Self
}


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

class Student: Person {}

var p = Person()

// Person
p.test()

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

上面提到的很多加了require,主要服务于子类,抽象

7.错误处理

7.1throws & rethrows
enum SomeError : Error {
    case illegalArgs(String)
    case outOfBounds(Int, Int)
    case outOfMemory
}


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

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

func test1() {
    do {
        try divide(num1: 10, num2: 0)
    } catch let error {
        switch error {
        case let SomeError.illegalArgs(msg):
            print("参数错误:",msg)
        default:
            print("其他错误")
        }
    }
}

// 1
// 2
// 参数异常 : 参数不能为0
// 4
  • 抛出error后,try下一句直到作用域结束的代码都将停止运行
  • 通过do-catch捕捉错误
  • 不捕捉错误,通过在函数增加throws声明,Error将自动往上抛
  • 如果是顶层函数,main函数依旧没有捕获error,那么程序就会终止

rethrows

 func exec(_ fn: (Int, Int) throws -> Int, _ num1: Int, _ num2: Int) rethrows {
    print(try fn(num1, num2))
}
// Fatal error: Error raised at top level
try exec(divide, 20, 0)
  • 其实rethrows只是一个声明,代表函数本身不会抛出错误,但是调用内部闭包会抛出错误,那么它将会把错误继续往上抛
7.2 try? & try!
print(try? divide(num1: 10, num2: 0)) // nil
print(try? divide(num1: 10, num2: 2)) // Optional(5)
print(try! divide(num1: 10, num2: 2)) // 5

等价
var a = try? divide(num1: 10, num2: 2)
var b: Int?
do{
	b = try divide(num1: 10, num2: 2)
} catch {
	b = nil
}

可以把?理解为可选链一样的意思,如果没有错误抛出,就调用,有错误就是nil,因此返回值必然是Optional类型的

7.3 defer
  • defer用来定义任何方式(抛错误,return等),离开代码块之前必须要执行的代码
  • 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(num1: 20, num2: 0)
}
// close将会在这里调用 }
try processFile("test.txt")
// open
// close
// Fatal error: Error raised at top level

// defer语句的执行顺序与定义顺序相反
func fn1() { print("fn1") }
func fn2() { print("fn2") }
func test3() {
    defer { fn1() }
    defer { fn2() }
}
test3()
// fn2
// fn1

8. 泛型

泛型可以将类型参数化,提高代码复用率,减少代码量

func swapVaues<T>(_ a: inout T, _ b: inout T) {
    (a, b) = (b, a)
}

var fn: (inout Double, inout Double) -> () = swapVaues

var n1 = 10
var n2 = 20
swapVaues(&n1, &n2)


var d1 = 20.0
var d2 = 30.0
fn(&d1, &d2)

类,结构体,枚举的泛型

// class
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> {}

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

// struct
struct Stack_S<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 }
}

// enum
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")
8.1 关联类型

类,结构体,枚举的泛型是上面这样的,但是protocol协议的泛型就不一样了,只能通过关联类型来处理,而且协议中支持多个关联类型

protocol Stackable {
    // 协议中的泛型,使用 associatedtype 关联类型来标记
    associatedtype Element
    // associatedtype ElementB
    mutating func push(_ element: Element)
    mutating func pop() -> Element
    func top() -> Element
    func size() -> Int
}

class StringStack: Stackable {
    typealias Element = String
    
    var elements = Array<String>()
    
    func push(_ element: String) {
        elements.append(element)
    }
    
    func pop() -> String {
        elements.removeLast()
    }
    
    func top() -> String {
        elements.last ?? ""
    }
    
    func size() -> Int {
        elements.count
    }
}

var strs = StringStack()
strs.push("qishu")
strs.push("kejing")


class Stack<E>: Stackable {
    typealias Element = E
    
    var elements = Array<E>()
    
    func push(_ element: E) {
        elements.append(element)
    }
    
    func pop() -> E {
        elements.removeLast()
    }
    
    func top() -> E {
        elements.last!
    }
    
    func size() -> Int {
        elements.count
    }
}
var ss = Stack<Int>()
ss.push(122)
ss.push(100)
8.2 类型约束
protocol Runable {}
class Person { }

func swapValues<T : Person & Runable>(_ 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>()
var stack3 = Stack<Int>()
// Global function 'equal' requires the types 'Int' and 'String' be equivalent
//equal(stack1, stack2)

print(equal(stack1, stack3))
8.3 协议泛型中的注意点,不透明类型(Opaque Type)

首先来看下返回遵守某个协议类型的函数

protocol Runable {}
class Person : Runable {}
class Car: Runable {}

func get(_ type: Int) -> Runable {
    if type == 0 {
        return Person()
    }
    return Car()
}

var r1 = get(0)
var r2 = get(1)

以上这种也是屏蔽了具体类型,开放Runable接口给外部。下面看下泛型协议,具有关联类型的代码

protocol Runable {
    associatedtype Speed
    var speed: Speed { get }
}
class Person : Runable {
    typealias Speed = Double
    var speed: Double { 1.0 }
}
class Car: Runable {
    typealias Speed = Int
    var speed: Int { 2 }
    
}

func get(_ type: Int) -> Runable {
    if type == 0 {
        return Person()
    }
    return Car()
}

var r1 = get(0)
var r2 = get(1)

报错如下
Protocol 'Runable' can only be used as a generic constraint because it has Self or associated type requirements
这边可以理解为,Runable泛型关联类型,get方法返回了Runable协议类型,但是由于实际类型有可可能是Car或者是Person,关联类型可能是Int,Double以及其他,总之返回的协议类型,没法确定关联类型的唯一性,因此报错。

解决方法:不透明类型

func get(_ type: Int) -> some Runable {
    if type == 0 {
        return Person()
    }
    return Car()
}

上面代码会报错
对于声明了some关键字的不透明类型,限制只能返回一种类型,这样返回值就能去确认唯一的关联类型了。

func get(_ type: Int) -> some Runable {
    return Car()
}

除了用在返回值上,还能在属性类型上

protocol Runable {
    associatedtype Speed
}

class Dog: Runable {
    typealias Speed = Double
}

class Person {
    var pet: some Runable {
        return Dog()
    }
}

可以看到Runable协议是泛型的,因此属性用到的地方,也需要申明some,标记为不透明类型,而且只返回一种类型。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值