- 1.Protocol
- 1.1Protocol语法
- 1.2格式
- 1.3对属性的使用 -1.4方法要求 -1.5Mutating方法要求 -1.6使用协议规范构造函数
- 1.7协议类型
- 3.1在扩展中添加协议成员
- 3.2通过扩展补充协议声明
- 3.3集合中的协议类型
- 3.4仅在类中使用协议 -4.协议的继承
- 4.1protocolComposition协议的合成
- 4.2检验协议的一致性
- 4.3可选协议要求
Protocol
- 协议:代理设计模式
- 协议能够被
类、枚举、结构体
实现
Protocol语法
定义协议格式
定义协议
protocol 协议名称 {
协议内容
}
//定义的协议只能被类遵守 定义格式
protocol 协议名称 : Class {
协议内容
}
复制代码
遵守协议
class 类名 : 协议1 , 协议n{
//类、结构体、枚举遵守协议时,在他们的名称后面加上协议名称中间用冒号分隔 多个协议之间用逗号分隔
}
class 类名:父类,协议1,协议n{
//类含有父类,则应当将父类放在所有的协议之前
}
复制代码
protocol Run {
func run()
}
protocol Eat {
func eat()
}
class Animal : Run , Eat {
func eat() {
print("吃啊")
}
func run() {
print("跑啊")
}
}
class Dog: NSObject,Run {
func run(){
print("狗在爬坡")
}
}
class Cat: Animal{
override func run(){
print("�� 在爬坡")
}
}
复制代码
对属性的使用
- 无论声明的属性为
类属性
还是对象属性
,均需要在属性声明后加上{get}
或{get set}
,代表属性只读
或读写
- 属性被声明为
{get}
,为可读的属性实现{setter}
方法,也不会报错。 - 在声明类属性时,需要在声明的属性前添加关键字
static
。 - 在实现者内,必须实现协议的类属性的
getter
方法,如果协议内定义的类属性是读写的,则还必须实现setter
方法 - 在协议中使用类型属性 总是使用
static
协议1
// 定义变量,必须在属性声明后添加{get}或者{get set}
protocol oneProtocol{
var argumentOne:Int{get} // 只读
var argumentTwo:Int{get set} // 读写
static var argumentClass:Int{get}
}
复制代码
类
class Person:oneProtocol{
var argumentOne: Int
var argumentTwo: Int
static var argumentClass: Int{
get{
return 30
}
}
init(argumentOne:Int,argumentTwo:Int) {
self.argumentOne = argumentOne
self.argumentTwo = argumentTwo
}
}
var per = Person(argumentOne: 90, argumentTwo: 1)
print("\(per.argumentOne)") // 90
print("\(Person.argumentClass)") // 30
复制代码
协议2
protocol Pro1{
var name:String{set get} //可读可写
var age:Int { get } //只读属性
static var status:Int {set get}
}
复制代码
遵循协议
struct Struct1 : Pro1 {
private var _name = ""
var name:String {
set{
_name = newValue
}
get{
return _name
}
}
var age = 18 //可以声明为可读可写的
static var status = 1 //类型方法。
}
class Class1: Pro1 {
var name = "class"
var age:Int{ return 19 } //也可以是只读
static var status = 0
}
复制代码
方法要求
- 协议内声明的方法不需要协议进行方法的实现
- 类方法,需要使用关键字
static
protocol funcProtocol{
func sayHello()
static func sayBad()
}
class Person:funcProtocol{
func sayHello() {
print("Hello")
}
static func sayBad() {
print("Bad")
}
}
复制代码
Mutating方法要求
- 能在方法或者函数内部改变字段的方法称为Mutating方法。
mutating
表示可以在该方法中修改它所属的实例及其实例属性的值- 一般mutating用在值拷贝的地方,例如:结构体、枚举。对于类或者类绑定协议中的方法无效
protocol Togglable{
mutating func toggle()
}
enum OnOffSwitch:Togglable{
case On,Off
mutating func toggle() {
switch self {
case .Off:
self = .On
case .On:
self = .Off
}
}
}
var light = OnOffSwitch.Off //Off
light.toggle() // On
struct OnOFF:Togglable{
var one:Int
var two:Int
mutating func toggle() {
if self.one > 10 {
self.two = 30
}else{
self.two = 20
}
}
}
var LIGHT = OnOFF(one: 2, two: 3)
print("\(LIGHT.two)") // 3
LIGHT.toggle()
print("\(LIGHT.two)") // 20
复制代码
protocol daysofaweek {
mutating func show()
}
enum days: daysofaweek {
case sun, mon, tue, wed, thurs, fri, sat
mutating func show() {
switch self {
case .sun:
self = .sun
print("Sunday")
case .mon:
self = .mon
print("Monday")
case .tue:
self = .tue
print("Tuesday")
case .wed:
self = .wed
print("Wednesday")
case .thurs:
self = .thurs
print("Wednesday")
case .fri:
self = .fri
print("Wednesday")
case .sat:
self = .sat
print("Saturday")
default:
print("NO Such Day")
}
}
}
var res = days.wed
res.show()
复制代码
使用协议规范构造函数
- 协议可以规定必须实现指定的构造函数,比如一些类中必须要求实现init构造函数,这样就可以制造一`个协议,让实现协议的类必须实现该构造函数。
- 实现构造协议的类,必须使用关键字
required
protocol TwoProtocol{
init(twoArgument:Int)
}
class two:TwoProtocol{
required init(twoArgument: Int) {
}
}
复制代码
- 父类中存在与协议内相同的构造方法,则子类在实现构造方法时,需要同时使用关键字
required
和override
protocol TwoProtocol{
init()
}
class TwoSuperClass{
init() {
}
}
class Two:TwoSuperClass,TwoProtocol{
required override init() {
}
}
复制代码
- 不能在final类中实现构造函数协议,因为final类是不能被继承的,因此不能实现构造函数协议
- 父类内被final修饰的方法与协议内相同,则该方法不能被遵循协议的子类实现
protocol TwoProtocol{
func say()
}
class TwoSuperClass{
final func say(){
print("Super Say Hello")
}
}
class Two:TwoSuperClass,TwoProtocol{
// 无法实现say方法
}
复制代码
协议类型
- 协议本身不实现任何功能,但是可以将它当作类型来使用。
- 协议作为类型使用的场景: ⒈作为函数、方法或构造器中的参数类型,返回值类型。 ⒉作为常量、变量、属性的类型。 ⒊作为数组、字典或其它容器中的元素类型
委托/代理设计模式
- 委托/代理是一种设计模式,它允许类或者结构体将一些需要它们负责的功能交由给其他类型。
- 委托模式的实现:定义协议来封装那些需要被委托的函数和方法,使实现者拥有数据源的类型
协议的使用
在扩展中添加协议成员
- 当无法修改原代码时,可以通过扩展来补充已经存在的类型。
- 扩展可以为已经存在的类型添加属性、方法、下标、协议等成员。
- 通过扩展为已存在的类型实现协议时,该类型的所有实例也会随之添加协议中的方法
protocol TextProtocol{
func printSomeMessage(message:String)
}
class Dice{
}
extension Dice:TextProtocol{
func printSomeMessage(message: String) {
print("\(message)")
}
}
let dic = Dice()
dic.printSomeMessage(message: "hello");
复制代码
通过扩展补充协议声明
- 当一个类型已经实现了协议中的所有要求,却没有声明时,可以通过扩展补充协议的声明。
protocol TextProtocol{
func printSomeMessage(message:String)
}
struct Hamster{
func printSomeMessage(message: String) {
print("\(message)")
}
}
extension Hamster:TextProtocol{
// 此时,Hamster的实例也可以作为TextProtocol类型使用
}
let hamster = Hamster()
复制代码
集合中的协议类型
- 协议也可以作为类型在集合中被使用,表示集合中的元素均为协议类型
let things:[TextProtocol] = [dic,hamster]
for thing in things {
thing.printSomeMessage(message: "Hello Word")
}
// 由于thing被当作是TextProtocol类型,故都能调用printSomeMessage方法
复制代码
仅在类中使用协议
- 通过在协议中增加class关键字,可以实现协议只让类来实现。
- 如果该协议同时继承其它协议,则需要在class后添加,并且用逗号隔开
protocol SomeOnlyClassProtocol:class{
}
// 协议SomeOnlyClassProtocol实现者只能是类
复制代码
协议的继承
- 协议可以继承一个或者多个其他协议,多个协议间用逗号隔开。
- 当类或者枚举等遵循协议时,不仅仅要实现当前协议的方法,也需要实现所遵循协议继承的其他协议
protocol OneProtocol{
func sayOne()
}
// 协议的继承
protocol TwoProtocol:OneProtocol{
func sayTwo()
}
class Person:TwoProtocol{
func sayOne() {
// 协议OneProtocol的方法
}
func sayTwo() {
// 协议TwoProtocol的方法
}
}
复制代码
protocolComposition协议的合成
- 一个协议可以由多个协议采用protocol<NamedProtocol,GenderProtocol>这样的格式进行组合,称之为协议的合成(protocol composition)。
- 协议合成并不会产生一个新的协议,而是将多个协议合成一个临时的协议,超出范围后立即失效
protocol NamedProtocol{
var name:String{get set}
}
protocol GenderProtocol{
var gender:String{get set}
}
protocol AgedProtocol{
var age:Int{get}
}
struct Person:NamedProtocol,GenderProtocol,AgedProtocol{
var name: String
var gender: String
var age: Int
}
func wishBirthday(celebrator:NamedProtocol & GenderProtocol){
print("姓名:\(celebrator.name),性别:\(celebrator.gender)")
}
var per = Person(name: "wang", gender: "man",age:20)
wishBirthday(celebrator: per)
// 形参celebrator的类型为protocol<NamedProtocol,GenderProtocol>,可以传入任何实现这两个协议的实例。即使此实例不仅仅遵循这两个协议。
// swift3.0之后,protocol<NamedProtocol,GenderProtocol>被NamedProtocol & GenderProtocol替换。
复制代码
检验协议的一致性
- 用is检验协议的一致性,使用as将协议类型向下转换为其他协议类型。
- is操作符用来检查实例是否实现了某个协议。返回值true/false。
- as?返回一个可选值,当实例实现协议时,返回该协议类型;否则返回nil。
- as用以强制向下转化类型。
- @objc用来表示协议时可选的,还可以表示暴露给Objective-C的代码,。
- @objc型协议只对类有效,因此只能在类中检查协议的一致性。
@objc 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: 20),
Country(area: 200),
Animal(legs: 10)
]
for object in objects {
var objectWithArea = object as? HasArea
if objectWithArea != nil {
print("\(objectWithArea)")
print("遵循了HasArea协议,\(object.area)")
}else{
print("没有遵循HasArea协议")
}
}
print("--------------------")
for object in objects {
// 返回值 true/false
var objectWithArea = object is HasArea
if objectWithArea {
print("\(objectWithArea)")
print("遵循了HasArea协议,\(object.area)")
}else{
print("没有遵循HasArea协议")
}
}
/**
打印结果:
Optional(__lldb_expr_386.Circle)
遵循了HasArea协议,Optional(1256.63708)
Optional(__lldb_expr_386.Country)
遵循了HasArea协议,Optional(200.0)
没有遵循HasArea协议
--------------------
true
遵循了HasArea协议,Optional(1256.63708)
true
遵循了HasArea协议,Optional(200.0)
没有遵循HasArea协议
*/
复制代码
可选协议要求
- 如果协议中声明的内容是可选的,也就是说在遵守协议时可以实现也可以不用实现
- 注意:在声明可选协议时,要在可选内容的前面加
@objc optional 关键字
并且在声明协议时在protocol关键字前加@objc
@objc protocol HasArea{
@objc optional var area:Double{get}
var width:Double{get}
}
class Circle:HasArea{
let pi = 3.1415927
var radius:Double = 10.0
var width: Double = 20.0
}
复制代码
检查协议的一致性
- 使用 is 操作符检查实例是否遵守了某个协议
- 使用 as 操作符把实例类型转换到指定的协议类型
stu1 is Run
stu1 is Stu
if let tempStu = stu1 as? Stu {
print("----------\(tempStu.name)")
} else {
print("************")
}
复制代码