Swift学习(十六):协议(protocol,static,class,mutating,init,init?,init!,继承,协议组合,as?,as!,Any,AnyObject,Self)

it2022-05-05  137

协议(Protocol)

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

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

协议中的属性

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

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


static、class

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

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


mutating

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

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


init

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

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

 

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

 


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){ } }

协议的继承

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

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


协议组合

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

可以给协议组合起别名:


CaseIterable

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


CustomStringConvertible

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

 print调用的是CustomStringConvertible协议的descriptiondebugPrint,po调用的是CustomDebugStringConvertible协议的debugDescription


Any、AnyObject

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

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

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


is、as?、as!、as

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

is判断类型:

as做强制类型转换:

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

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

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

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

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


X.self、X.Type、AnyClass

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

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

class Person { } var p: Person = Person() var pType: Person.Type = Person.self

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

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

X.self属于X.Type类型

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

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

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

  等价于 

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


元类型的应用

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

 

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代表当前类型

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

 

 

 

 

 

 

 

 


最新回复(0)