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

协议(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协议的description
  • debugPrint,po调用的是CustomDebugStringConvertible协议的debugDescription


Any、AnyObject

  • Swift提供了2种特殊的类型:Any、AnyObject
  • Any:可以代表任意类型(枚举、结构体、类,也包括函数类型) 
  • 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())

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章