协议(Protocol)

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

swift 协议 set get_init、init?、init!

swift 协议 set get_mutating_02

  • 协议中定义方法时不能有默认参数值
  •  默认情况下,协议中定义的内容必须全部都实现
  • 也有办法可以办到只实现部分内容

协议中的属性

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

swift 协议 set get_init、init?、init!_03

swift 协议 set get_init、init?、init!_04

 这里的var x: Int = 0可以看作是实现了set和get,因为可以赋值和取值;let y: Int = 0由于不能修改值,可以看作只实现了get,也可以使用下图的计算属性方式实现:

swift 协议 set get_mutating_05


static、class

为了保证通用,协议中必须用static定义类型方法、类型属性、类型下标

swift 协议 set get_Swift_06

实现的时候使用class则代表方法可以被继承,static则代表方法不能被继承


mutating

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

swift 协议 set get_init、init?、init!_07

  protocol中不加mutating,而结构体里加了mutating,结构体会报错,protocol对于结构体而言需要加mutating


init

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

swift 协议 set get_Swift_08

加required的原因是因为这样可以要求继承自Point的类都必须实现init,从而达到所有遵守Drawable协议的类都实现init,final不需要加是因为加final的类不允许被继承。

 

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

 

swift 协议 set get_Swift_09


init、init?、init!

  • 协议中定义的init?、init!,可以用init、init?、init!去实现 
  • 协议中定义的init,可以用init、init!去实现
protocol Liveable {
    init()
    init?(age: Int)
    init!(no: Int)
}

class Person: Liveable {
    
    required init() {
    }
    //init()第二种写法
//    required init!() { }
    
    required init?(age: Int) {
        
    }
    //init?(age: Int)第二种写法
//    required init(age: Int) { }
//    required init!(age: Int){ }
    
    required init!(no: Int) {
        
    }
    //init!(no: Int)第二种写法
//    required init(no: Int){ }
//    required init?(no: Int){ }
}

协议的继承

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

swift 协议 set get_协议_10

可以看出,遵守Livable协议的Person类必须实现Runnable和Livable都有的方法。


协议组合

  • 协议组合,可以包含一个类类型(最多一个)

swift 协议 set get_Swift_11

swift 协议 set get_swift 协议 set get_12

可以给协议组合起别名:

swift 协议 set get_mutating_13


CaseIterable

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

swift 协议 set get_Swift_14


CustomStringConvertible

  • 遵守CustomStringConvertible协议,可以自定义实例的打印字符串

swift 协议 set get_Swift_15

  •  print调用的是CustomStringConvertible协议的description
  • debugPrint,po调用的是CustomDebugStringConvertible协议的debugDescription

swift 协议 set get_Swift_16


Any、AnyObject

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

stu属于Any类型,可以赋任何值:

swift 协议 set get_swift 协议 set get_17

[Any]():创建可以存放任何类型的数组

swift 协议 set get_swift 协议 set get_18


is、as?、as!、as

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

is判断类型:

swift 协议 set get_协议_19

swift 协议 set get_协议_20

as做强制类型转换:

swift 协议 set get_协议_21

(stu as? Student)?.study():第一个?代表stu可能强制转化为Student,也可能失败,第二个?代表可选类型要调用方法,需要加?

(stu as? Student)!.study() 等同于 (stu as! Student).study()

当确定一定会转换成功用as,否则用as?

swift 协议 set get_mutating_22

  Int("123")一定可以转化为Any,所以用as

swift 协议 set get_mutating_23

  10一定可以转换为Double,所以用as


X.self、X.Type、AnyClass

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

swift 协议 set get_init、init?、init!_24

对象的指针指向的堆内存的前8个字节存放着类型相关信息,可以把前8个字节认为是元类型的指针,指向元类型metadata

class Person {
    
}

var p: Person = Person()
var pType: Person.Type = Person.self

反汇编上面代码,查看pType存储的到底是什么:

swift 协议 set get_Swift_25

swift 协议 set get_swift 协议 set get_26

根据两个断点处的rax对比我们可以看出,代表p对象内存地址的第二个rax中的前8个字节与代表pType的第一个rax相同,说明pType存储的是p对象的metadata。

  • X.self属于X.Type类型

swift 协议 set get_协议_27

由上图看出,由于Student继承自Person,所以pType = Student.self也成立,好比多态的父类指针指向子类对象,但是不继承自Person的类不可以这么写

  • 在Swift底层方法里,有这么个定义

swift 协议 set get_swift 协议 set get_28

  代表着AnyClass就是AnyObject.Type,是任意类类型的Type

swift 协议 set get_mutating_29

  等价于 

swift 协议 set get_init、init?、init!_30

  • type(of: )获取元类型的指针

swift 协议 set get_协议_31


元类型的应用

为什么Animal的init(){ }要用required修饰?因为这是要求子类必须有的,实现的方法,万一子类重写init相关的方法,就会造成子类里不会自动继承父类的init,这时添加required,是强制子类实现init()方法,这样就会保证子类可以调用init()方法

swift 协议 set get_mutating_32

 

swift 协议 set get_init、init?、init!_33

class_getInstanceSize:表示对象实际用到的内存大小,也就是存储属性需要的最小值

class_getSuperclass:获取父类,我们可以看到Person虽然没继承任何类,但它有一个要隐藏的基类Swift._SwiftObject

  • 从结果可以看得出来,Swift还有个隐藏的基类:Swift._SwiftObject 
  • 可以参考Swift源码:https://github.com/apple/swift/blob/master/stdlib/public/runtime/SwiftObject.h

Self

  • Self代表当前类型

swift 协议 set get_协议_34

 Self.count 等同于Person.count

  • Self一般用作返回值类型,限定返回值跟方法调用者必须是同一类型(也可以作为参数类型)
protocol Runnable {
    func test () -> Self
}

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

class Student: Person { }

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

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