you can extend a protocol to implement some of these requirements or to implement additional functionality that conforming types can take advantage of.
语法
protocol SomeProtocol {
// protocol definition goes here
}
struct SomeStructure: FirstProtocol, AnotherProtocol {
// structure definition goes here
}
class SomeClass: SomeSuperclass, FirstProtocol, AnotherProtocol {
// class definition goes here
}
协议属性
任何实现协议的类型,都需要为协议提供带有名称和类型的实例属性或类型属性,并限定读写权限,协议本身不会限定属性是存储属性还是计算属性。
var poperty_name: type { get or set }
实例属性
协议 SomeProtocol 定义了两个实例属性,读写属性和只读属性,用 var 声明为变量,:
protocol SomeProtocol {
var mustBeSettable: Int { get set }
var doesNotNeedToBeSettable: Int { get }
}
let 在属性限定中不可用
协议 FullyNamed 定义了读写 fullName 属性,结构 Person 实现了该协议,系统会自动为期生产 getter 方法和 setter 方法:
protocol FullyNamed {
var fullName: String { get set }
}struct Person: FullyNamed {
var fullName: String
}
let john = Person(fullName: "John Appleseed")
// john.fullName is "John Appleseed"
类型属性
在实例属性限定的基础上加上 static 修饰符,定义类型属性:
protocol AnotherProtocol {
static var someTypeProperty: Int { get set }
}
协议方法
协议中可定义实例方法和类型方法,支持可变参数。
但是无法在协议方法中指定参数的默认值。
类型方法
同类型属性,使用 static 定义类型方法
protocol SomeProtocol {
static func someTypeMethod()
}
实例方法
protocol RandomNumberGenerator {
func random() -> Double
}
class LinearCongruentialGenerator: RandomNumberGenerator {
var lastRandom = 42.0
let m = 139968.0
let a = 3877.0
let c = 29573.0
func random() -> Double {
lastRandom = ((lastRandom * a + c)
.truncatingRemainder(dividingBy:m))
return lastRandom / m
}
}
let generator = LinearCongruentialGenerator()
print("Here's a random number: \(generator.random())")
// Prints "Here's a random number: 0.3746499199817101"
print("And another one: \(generator.random())")
// Prints "And another one: 0.729023776863283"
值类型的可变实例方法
有时一个方法需要修改它隶属的值类型的实例,可以在 func 之前放置 mutating 关键字,允许方法**修改其隶属的实例和实例的属性,**具体的修改过程请参考Modifying Value Types from Within Instance Methods。
mutating + 方法声明语法
可变实例方法
protocol Togglable {
mutating func toggle()
}
枚举 OnOffSwitch 实现协议 Togglable,通过修改类型的状态,完成实例状态的切换。
enum OnOffSwitch: Togglable {
case off, on
mutating func toggle() {
switch self {
case .off:
self = .on
case .on:
self = .off
}
}
}
var lightSwitch = OnOffSwitch.off
lightSwitch.toggle()
// lightSwitch is now equal to .on
初始化方法
协议可以限定需要实现的初始化方法,形式如下:
protocol SomeProtocol {
init(someParameter: Int)
}
使用关键字 required ,把协议限定的初始化方法,指定为指定初始化方法或便捷初始化方法:
class SomeClass: SomeProtocol {
required init(someParameter: Int) {
// initializer implementation goes here
}
}
对于使用 final 修饰的类,可以省去初始化方法实现中的 required 修饰符,可参考 Required Initializers 和 Preventing Overrides
对于 final 修饰的类,协议初始化方法不需要使用 required 修饰符。如果父类和子类自己实现的协议中有相同初始化方法,需要同时保留 **required 和 override **两个关键字。
protocol SomeProtocol {
init()
}class SomeSuperClass {
init() {
// initializer implementation goes here
}
}class SomeSubClass: SomeSuperClass, SomeProtocol {
// "required" from SomeProtocol conformance; "override" from SomeSuperClass
required override init() {
// initializer implementation goes here
}
}
可以使用 final 限定方法、属性和下标禁止被子类覆盖
协议可以定义可失败初始化方法,即返回 nil 的方法。可失败和非可失败初始化方法,都可以满足可失败初始化方法。非可失败和隐式未打包可失败初始化方法,都可以满足非可失败初始化方法。
struct Animal {
let species: String
init?(species: String) {
if species.isEmpty { return nil }
self.species = species
}
}
协议的使用
协议作为类型
协议是类型,可以作为参数、返回值、常量、变量、属性等元素的类型。可以通过 AnyObject 协议,限定自定义协议只能由类实现,排除枚举和结构体。
protocol SomeClassOnlyProtocol: AnyObject, SomeInheritedProtocol {
// class-only protocol definition goes here
}
协议的组合
协议可以继承多个协议,类可以继承多个协议。
类只能继承一个父类,并且要放置在继承列表的第一个位置,其后可以跟随多个协议。
检测与强转
Type Casting 中的is 操作符检测类型是否实现了某个协议,as 操作符进行协议类型强转。
-
is操作符 返回true如果实例实现了某协议,否则返回false -
as?向下转换操作符号的 + ? 会返回一个类型的可选值,如果转换失败返回nil -
as!向下转换操作符号的 + ! 会返回一个强转后的协议类型,如果强转失败会触发一个运行时错误(很危险,会引起对象内存模型的混乱表达)protocol HasArea { var area: Double { get } }
class Circle: HasArea { let pi = 3.1415927 var radius: Double var area: Double { return pi * radius * radius } init(radius: Double) { self.radius = radius } } class Country: HasArea { var area: Double init(area: Double) { self.area = area } }
class Animal { var legs: Int init(legs: Int) { self.legs = legs } }
let objects: [AnyObject] = [ Circle(radius: 2.0), Country(area: 243_610), Animal(legs: 4) ]
for object in objects { if let objectWithArea = object as? HasArea { print("Area is (objectWithArea.area)") } else { print("Something that doesn't have an area") } } // Area is 12.5663708 // Area is 243610.0 // Something that doesn't have an area
可选协议
**Swift 引入通过 objc 关键字,实现与 **Objective-C 混编;通过引用 optional 关键字定义可选协议,引入 Objective-C 中的可选方法,由实现的类型决定是否实现其中声明的限制。optional 关键字不可独立使用。
@objc protocol ProtocolName {
@objc optional ...}
非可选协议中的所有限制必须由类型实现。可选协议只能由 Objective-C 子类实现,枚举和结构体不能实现。
class ThreeSource: NSObject, ProtocolName {}
可选协议中的属性或是方法声明会自动变为 optional,如 (Int) -> String 会变为((Int) -> String)?。
Note that the entire function type is wrapped in the optional, not the method’s return value.
可选链 optional chaining
可以在可选链中使用可选协议。属性名称+?或是方法名称+?+ 参数,如果属性值为 nil或是方法没有实现,调用链终止,详情可参考 Optional Chaining 。
@objc protocol CounterDataSource {
@objc optional func increment(forCount count: Int) -> Int
@objc optional var fixedIncrement: Int { get }
}
class Counter {
var count = 0
var dataSource: CounterDataSource?
func increment() {
if let amount = dataSource?.increment?(forCount: count) {
count += amount
} else if let amount = dataSource?.fixedIncrement {
count += amount
}
}
}
协议扩展
可以对协议进行扩展,为实现协议的类型提供同一的方法、初始化方法、下标和计算属性的实现。如下面 RandomNumberGenerator 的扩展,为所有实现 RandomNumberGenerator 协议的类型提供了 randomBool() -> Bool 方法。
extension RandomNumberGenerator {
func randomBool() -> Bool {
return random() > 0.5
}
}
let generator = LinearCongruentialGenerator()
print("Here's a random number: \(generator.random())")
// Prints "Here's a random number: 0.3746499199817101"
print("And here's a random Boolean: \(generator.randomBool())")
// Prints "And here's a random Boolean: true"
默认实现
可以通过扩展,为方法和计算属性提供默认实现。
extension PrettyTextRepresentable {
var prettyTextualDescription: String {
return textualDescription
}
}
添加限制
在协议扩展中,可以在协议名称后面,使用范型中的 where 语句,指定类型必须满足的限制,更多信息可参考 Generic Where Clauses 。
extension ProtocolName where Element1: Protocol1, Element2: Protocol2, ... {
}
or
extension Moveable where Element1: protocol<Protocol1, Protocol2>, ... {
}
When you define a protocol extension, you can specify constraints that conforming types must satisfy before the methods and properties of the extension are available.
下面例子限定集合中的元素要实现 Equatable 协议,因此可以使用== 和!=操作符判断两个元素是否相等:
extension Collection where Element: Equatable {
func allEqual() -> Bool {
for element in self {
if element != self.first {
return false
}
}
return true
}
}
小结
Swift 协议遵循多继承原则,只能继承一个父类,可以通过 AnyObject 协议,限定自定义协议只能由类实现,关闭枚举和结构体实现协议的权限。
资料
官方文档
docs.swift.org/swift-book/… 范型
docs.swift.org/swift-book/… 选择值列表
docs.swift.org/swift-book/… 类型转换
docs.swift.org/swift-book/… 初始化方法
三方资料
medium.com/@agrawalsun… 值类型的可变实例方法