Protocol

相关资料1 相关资料2 相关资料3 4

-2.委托/代理设计模式 -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) {
    }
}
复制代码
  • 父类中存在与协议内相同的构造方法,则子类在实现构造方法时,需要同时使用关键字requiredoverride
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("************")
}
复制代码

转载于:https://juejin.im/post/5c109bed518825642650b9c0

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值