属性要求
-
协议可以要求遵循协议的类型提供特定名称和类型的实例属性或类型属性
-
协议总是用
var
关键字来声明变量属性,在类型声明后加上{ set get }
来表示属性是可读可写的,可读属性则用{ get }
来表示:
如果协议要求属性是可读可写的,那么该属性不能是常量属性或只读的计算型属性。如果协议只要求属性是可读的,那么该属性不仅可以是可读的,如果代码需要的话,还可以是可写的。
protocol NickName {
var nickName:String{get set} // 用 var 关键字来声明
var privacy:String{get } // 协议中的只读属性,实现时可以根据实际情况让属性可读可写
}
class Person : NickName {
var nickName: String { // 协议中有可读可写属性,实现时必须也是可读可写
set {
}
get {
return ""
}
}
var privacy: String // 协议中声明的属性,可以通过计算属性或存储属性来实现
init() {
privacy = ""
nickName = ""// init 中使用计算属性,必须是在存储属性都初始化完成后
}
}
方法要求
- 可以要求遵循协议的类型实现某些指定的实例方法或类方法
- 不支持为协议中的方法提供默认参数
- 定义类方法的时候,总是使用
static
关键字作为前缀。即使在类实现时,类方法要求使用class
或static
作为关键字前缀
异变方法的要求
protocol ChangeSelf {
mutating func change() // 改变本身
}
class Person : ChangeSelf {
func change() { // 类实现不需要携带 mutating关键字
print("some")
}
}
enum X:ChangeSelf {
case on,off
mutating func change() { // 值类型实现必须加 mutating
switch self {
case .on:
self = .off
default:
self = .on
}
}
}
构造器的要求
-
在遵循协议的类中实现构造器,无论是作为指定构造器,还是作为便利构造器。无论哪种情况,你都必须为构造器实现标上
required
修饰符如果类已经被标记为
final
,那么不需要在协议构造器的实现中使用required
修饰符,因为final
类不能有子类。 -
一个子类重写了父类的指定构造器,并且该构造器满足了某个协议的要求,那么该构造器的实现需要同时标注
required
和override
修饰符 -
遵循协议的类型可以通过可失败构造器(
init?
)或非可失败构造器(init
)来满足协议中定义的可失败构造器要求。协议中定义的非可失败构造器要求可以通过非可失败构造器(init
)或隐式解包可失败构造器(init!
)来满足。
协议作为类型
协议作为类型使用,有时被称作「存在类型」,这个名词来自「存在着一个类型 T,该类型遵循协议 T」。
- 作为函数、方法或构造器中的参数类型或返回值类型
- 作为常量、变量或属性的类型
- 作为数组、字典或其他容器中的元素类型
委托
protocol PushDelegate { // 1.全局创建协议
func pushToNewsDetailVc(newId:Int)
}
class NewCell {
var delegate : PushDelegate?// 2.需要委托的地方定义一个代理属性
func didSelect() {
delegate?.pushToNewsDetailVc(newId: 12306)
}
}
class NewsViewController:PushDelegate {// 3.代理遵循协议 + 实现
func pushToNewsDetailVc(newId: Int) {
print("跳转详情页了",newId)
}
}
let cell = NewCell()
let vc = NewsViewController()
cell.delegate = vc // 4.将委托者与代理关联
cell.didSelect()
在扩展里增加协议遵循
扩展可以为已有类型添加属性、方法、下标以及构造器,因此可以符合协议中的相应要求。
有条件的遵循协议
通过 泛型 where
分句 实现
在扩展中采纳协议
protocol Play {
func play()
}
class Person {
func play(){
print("doSome")
}
}
extension Person:Play {
// 在扩展中采纳协议,这样类型就能作为遵循协议的类型来使用,并且不需要额外实现
}
使用合成实现来采纳协议
-
使用合成实现(默认实现)之后,无需再编写重复的代码来实现这些协议所要求的方法。
-
Swift 自动提供一些简单场景下遵循
Equatable
、Hashable
和Comparable
协议的实现-
Equatable
-
遵循
Equatable
协议且只有存储属性的结构体。struct S2:Equatable { var name:String var jump:Int = 0 var age:Int { // 采纳系统 Equatable协议的结构体可以声明计算属性 set { jump = newValue } get { return jump } } } var A = S2.init(name: "a") var C = S2.init(name: "a") if A == C { // 会对比所有的存储属性 }
-
遵循
Equatable
协议且只有关联类型的枚举 -
没有任何关联类型的枚举
-
-
类的专属协议
通过添加
AnyObject
关键字到协议的继承列表,就可以限制协议只能被类类型采纳(以及非结构体或者非枚举的类型)。
协议合成
protocol Name {
var name:String{set get}
}
protocol NickName {
var nickName:String {set get}
}
protocol Age:AnyObject { // AnyObject 类的专属协议
var age:Int{get set}
}
class AClass:Name,NickName {
var name = "AClass"
var nickName = "ANickName"
}
class ASubclass: AClass,Age {
var age: Int = 18
}
struct BStruct:Name,NickName {
var name = "BStruck"
var nickName = "BNickName"
}
func printInfo(user info:Name&NickName){ // 通过 & 进行 协议合成
print(info.name,info.nickName)
}
func printEverything(user info:AClass&Age){ // 通过 & 进行 类+协议合成
print(info.name,info.nickName,info.age)
}
let Sub = ASubclass()
printInfo(user: Sub)
printEverything(user: Sub)
检查协议一致性
-
is
用来检查实例是否遵循某个协议,若遵循则返回true
,否则返回false
; -
as?
返回一个可选值,当实例遵循某个协议时,返回类型为协议类型的可选值,否则返回nil
; -
as!
将实例强制向下转换到某个协议类型,如果强转失败,将触发运行时错误。
可选的协议要求
使用可选要求时(例如,可选的方法或者属性),它们的类型会自动变成可选的。比如,一个类型为
(Int) -> String
的方法会变成((Int) -> String)?
。需要注意的是整个函数类型是可选的,而不是函数的返回值
@objc protocol Name {
var name:String{set get}
@objc optional var nickName:String{set get}
}
class Person:Name {
var name: String = "Person"
}
协议的扩展
- 协议可以通过扩展来为遵循协议的类型提供属性、方法以及下标的实现。
- 协议扩展可以为遵循协议的类型增加实现,但不能声明该协议继承自另一个协议。协议的继承只能在协议声明处进行指定
默认实现:如果遵循协议的类型为这些要求提供了自己的实现,那么这些自定义实现将会替代扩展中的默认实现被使用。
网友评论