iOS开发 - 「Swift 学习」Swift协议

Swift协议

协议规定了用来实现某一特定功能所必需的方法属性

类、结构体、枚举类型都可以遵循协议,并提供具体实现来完成协议定义的方法和功能。
能够满足协议要求的类型被称为遵循此协议

(一)协议的声明\color{red}{(协议本身是不实现任何功能的)}

协议的定义方式(关键词:protocol):

protocol ProtocolName {
    // 协议内容(声明属性/方法)
}

协议对属性声明的规定

协议用于指定特定的实例属性或类属性,而不用指定是存储型还是计算型的属性。必须要指明是只读的还是可读可写的属性

通常用var关键词声名变量属性,在类型声明后面加{set get}表示是可读可写属性,用{get}表示只读属性

protocol firstProtocol {
    //声明协议属性(可读可写)
    var english:Float{set get}
    var chinese:Double{set get}
    var mathematics:Double{set get}
    //声明协议方法
    func minResults() -> String
    func maxResults() -> String
}
//协议的继承
protocol secondProtocol:firstProtocol{
     //声明只读型的协议属性
    var ranking:String{get}
    var name:String{get}
}

//FirstClass类遵循secondProtocol协议,需要FirstClass类实现secondProtocol协议中的属性及方法
class FirstClass:secondProtocol {
    //实现协议中的只读属性
    let ranking = "第三名"
    let name = "小明"
    
    var english:Float = 78.50
    
    var chinese = 88.0
    
    var mathematics = 95.0
    
    func minResults() -> String {
        return "小明本次成绩数学是最高分95.0"
    }
    
    func maxResults() -> String {
        return "小明本次成绩最低分是英语78.5"
    }
}

调用

let student = FirstClass()
print("\(student.maxResults())")

调用结果

小明本次成绩最低分是英语78.5

协议对构造器的声明

协议是可以要求它的遵循者实现指定的构造器
在协议的定义中\color{red}{只需要写下构造器的声明部分},不需要花括号及构造器的实体

*/
protocol ProtocolName {
   init(someParameter: Int)
}
*/

protocol tcpprotocol {
    init (aprot:Int)
}

协议中对Mutating方法的规定

若需要在方法中改变实例,例如,值类型(结构体,枚举)的实例方法中,将mutating关键字作为函数的前缀,写在func之前,表示可以在该方法中修改它所属的实例及其实例属性的值

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("Firday")
        case .sat:
            self = .sat
            print("Saturday")
        default:
            print("NO Such Day")
        }
    }
}

调用

var res = days.wed
res.show()

调用结果

Wednesday

(二)协议的实现

协议构造器在类中的实现

在遵循该协议的类中实现构造器,并指定其为类的指定构造器或便利构造器,必须给\color{red}{构造器的实现}"required"修饰

//协议构造器的实现
class SomeClass: ProtocolName {
   required init(someParameter: Int) {
      // 构造器实现
   }
}
protocol tcpprotocol {
    init (aprot:Int)
}

class tcpClass:tcpprotocol {
    required init(aprot: Int) {
        print("实现指定构造器方法")
    }
}

class TestMainClass {
    var num:Int//局部变量
    init(aprot:Int){
        self.num = aprot
    }
}

class TestSubClass:TestMainClass,tcpprotocol {
    var num2:Int
    init(num1:Int,num2:Int){
        self.num2 = num2
        super.init(aprot: num1)
    }
    //遵循协议,加上"required" 继承父类重写父类构造器加上"override"
    required override  convenience init(aprot: Int) {
        self.init(num1: aprot, num2: 10)
    }
}

调用

let tcp = tcpClass(aprot: 3)
print("实现协议方法:\(tcp)")

调用结果

实现指定构造器方法
实现协议方法:Swift_study.tcpClass

协议类型

\color{red}{协议本身是不实现任何功能的},但是协议可以被当做类型使用
\color{red}{✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧}
\color{BlueViolet}{协议类型的使用场景:}
\color{red}{✎} \color{BlueViolet}{作为函数、方法或构造器中的参数类型或返回值类型}
\color{red}{✎} \color{BlueViolet}{作为常量、变量或属性的类型}
\color{red}{✎} \color{BlueViolet}{作为数组、字典或其他容器中的元素类型}
\color{red}{✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧}

protocol TestProtocolA{
    var num:Int{get set}
    func calc(sum:Int)
}

protocol ResultProtocol{
//将协议TestProtocolA作为定义方法时的参数类型
    func print(target: TestProtocolA)
}

class XiaoHong:ResultProtocol{
    func print(target: TestProtocolA) {
        target.calc(sum: 1)
    }
}

class XiaoQiang:ResultProtocol {
    func print(target: TestProtocolA) {
        target.calc(sum: 5)
    }
}

class DaMing:TestProtocolA {
    var num: Int = 10
    
    func calc(sum: Int) {
        num -= sum
        print("大明尝试\(sum)次通过")
    
        if num <= 0{
            print("大明缺席考试")
        }
    }
}

class Player {
    var stmark:ResultProtocol!
    
    init(stmark:ResultProtocol){
        self.stmark = stmark
    }
    
    func print(target:TestProtocolA){
        stmark.print(target: target)
    }
}

调用

let marks = Player(stmark: XiaoHong())
let marksec = DaMing()
marks.print(target: marksec)
        
marks.stmark = XiaoQiang()
marks.print(target: marksec)
marks.print(target: marksec)
marks.print(target: marksec)

调用结果

大明尝试1次通过
大明尝试5次通过
大明尝试5次通过
大明缺席考试
大明尝试5次通过
大明缺席考试

在扩展中添加遵循的协议

扩展可以为已存在的类型添加\color{DarkOrange}{属性、方法、下标脚本、} \color{red}{协议}等成员

//在扩展中添加协议成员
enum ageType
{
    case Baby,Child,Teenager,Young,Elderly,Normal
}

protocol AgeClasificationProtocol {
    var age: Int { get }
    func agecClassified() -> ageType
}

//创建Actor类
class Actor {

    let firstname:String
    let lastname:String
    //很神奇,对Actor扩展时增加了AgeClasificationProtocol协议,在此实现协议里的属性、方法也是可以的(也可以在扩展中实现)
    var age:Int
    init(firstname:String,lastname:String) {
        self.firstname = firstname;
        self.lastname = lastname;
        self.age = 13
    }
}

//对Actor类扩展时添加AgeClasificationProtocol协议
extension Actor:AgeClasificationProtocol{
    
    func fullname() -> String {
        var name: String
        name = firstname + " " + lastname
        return name
    }
    
    //实现协议方法
    func agecClassified() -> ageType {
        switch age{
        case 0...2:
            return .Baby
        case 3...12:
            return .Child
        case 13...19:
            return .Teenager
        case 20...40:
            return .Young
        case let x where x > 65:
            return .Elderly
        default:
            return .Normal
        }
    }
    
    // () -> ageType 函数类型的参数
    func ageTypeName(typeFunc:() -> ageType) -> String {
        let type = typeFunc()
        
        switch type {
        case .Baby:
            return "婴儿"
        case .Child:
            return "小孩儿"
        case .Teenager:
            return "少年"
        case .Young:
            return "青年"
        case .Elderly:
            return "长者"
        default:
            return "未知"
        }
    }
}

调用

let xiaoming = Actor(firstname: "王", lastname: "小明")
xiaoming.age = 12
let ageName = xiaoming.ageTypeName(typeFunc: xiaoming.agecClassified)
print("演员的全名:\(xiaoming.fullname())")
print("\(xiaoming.fullname()):所属的年龄段:\(ageName)")

调用结果

演员的全名:王 小明
王 小明:所属的年龄段:小孩儿

协议的继承

协议是能够继承\color{red}{一个或者多个}其他协议,可以在继承的协议基础上增加新的内容要求。
语法:协议的继承语法与类的继承相似,\color{red}{多个被继承的协议之间用逗号隔开}

//protocol 新的协议名: 被继承的协议, 被继承的协议,其他被继承的协议 { // 增加的新的协议定义}
protocol NewProtocolName: SomeInheritingProtocol, AnotherInheritingProtocol {
    // 增加的新的协议定义
}

protocol firstProtocol {
    
    var english:Float{set get}
    var chinese:Double{set get}
    var mathematics:Double{set get}
    
    func minResults() -> String
    func maxResults() -> String
}

protocol secondProtocol:firstProtocol{
    
    var ranking:String{get}
    var name:String{get}
}


class FirstClass:secondProtocol {
    
    let ranking = "第三名"
    
    let name = "小明"
    
    var english:Float = 78.50
    
    var chinese = 88.0
    
    var mathematics = 95.0
    
    func minResults() -> String {
        return "小明本次成绩数学是最高分95.0"
    }
    
    func maxResults() -> String {
        return "小明本次成绩最低分是英语78.5"
    }
}

调用

let student = FirstClass()
print("\(student.maxResults())")

调用结果

小明本次成绩最低分是英语78.5

定义类的专属协议

可以在协议的继承列表中,通过\color{red}{添加class关键字,限制协议只能适配到类(Class)类型}

注意:class关键字必须是\color{red}{第一个出现在协议的继承列表中},其后,才是其他继承协议。

protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol {
// 协议的定义内容
}

protocol TcpProtocol {
    init(num:Int)
}
//用class修饰符在协议的继承列表中定义类的专属协议ExclusiveProtocol
protocol ExclusiveProtocol:class,TcpProtocol{
    init(num1:Int,num2:Int)
}
//TcpProtocol协议可以被结构体遵循
struct School:TcpProtocol{
    init(num: Int) {
        print("市第\(num)中学")
    }
}
//ExclusiveProtocol协议已经不能被结构体类型遵循了
/*
struct Hospital:ExclusiveProtocol {
    init(num: Int) {
        print("市第\(num)医院")
    }
}
*/

//Hospital类遵循专属协议ExclusiveProtocol
class Hospital:ExclusiveProtocol{
    
    var area:Int
    var num:Int
    init(area: Int, num: Int) {
        self.area = area
        self.num = num
        print("\(area)区-\(num)号医院")
    }

    required convenience init(num1: Int, num2: Int) {
        self.init(area: num1, num: num2)

    }

    required convenience init(num: Int) {
        self.init(area: 5, num: num)
    }
}


class CityHospital:Hospital{
    var address:String
    init(address:String,area:Int,num:Int){
        self.address = address
        super.init(area: area, num: num)
        
        print("\(address)-\(area)区-\(num)号医院")
    }
    
    required convenience init(num1: Int, num2: Int) {
        self.init(address:"河北街",area: num1, num: num2)
    }
    
    required convenience init(num: Int) {
        self.init(address:"河东街",area: num, num: 10)
    }
}

调用

let hospital = Hospital(num: 8);
let hospital_a = CityHospital(address: "河西街", area: 1, num: 2)
let hospital_b = CityHospital(num: 6)

调用结果

5区-8号医院
1区-2号医院
河西街-1区-2号医院
6区-10号医院
河东街-6区-10号医院

协议的合成

\color{red}{Swift支持合成多个协议},在需要同时遵循多个协议时很有用

protocol NameProtocol {
    var name:String{get}
}

protocol AgeProtocol {
    var age:Int{get}
}
//Worker遵循俩协议
struct Worker:NameProtocol,AgeProtocol {
    var name: String
    var age: Int
}

//NameProtocol & AgeProtocol合体
func showWorker(worker:NameProtocol & AgeProtocol){
    print("\(worker.name) is \(worker.age) years old")
}

调用

let zhangzong = Worker(name:"老张", age: 45)
print(zhangzong)
showWorker(worker: zhangzong)

调用结果

Worker(name: "老张", age: 45)
老张 is 45 years old

检验协议的一致性

使用isas操作符检查是否遵循某协议或强制转化为某一协议类型

protocol HasArea {
    var area:Double{get}
}
//圆
class Circular:HasArea {
    let pi = 3.14159265
    var radius:Double
    var area: Double {
        return pi * radius * radius
    }
    init(radius:Double){
        self.radius = radius
    }
}


class Rectangular:HasArea {
    var width:Double
    var height:Double
    
    var area: Double{
        return width * height
    }
    
    init(width:Double,height:Double){
        self.width = width
        self.height = height
    }
}

class Hous{
    var rooms:Int = 0
    func configure(config: (Hous) -> Void) -> Hous {
        config(self)
        return self
    }
    
    func rooms(_ value: Int) -> Self {
        
        self.rooms = value
        return self
    }
}

调用

let hous = Hous().configure{ $0.rooms = 3 }
        print(hous.rooms)
        
        
        let objs:[AnyObject] = [Circular(radius: 2.4),
                                Rectangular(width: 5, height: 3),
                                Hous().rooms(3),
                                Hous().configure(config: {hous in
            hous.rooms = 5
        })]
        
        for obj in objs {
            if let objItem = obj as? HasArea{
                print("面积为:\(objItem.area)")
            }else {
                print("没有面积概念")
            }
        }
        
        
        let pro = Hous().rooms(3)
        if pro is HasArea{
            //print("矩形的面积:\(pro.area)")
        }else {
            print("有\(pro.rooms)个房间")
        }

调用结果

3
面积为:18.095573664
面积为:15.0
没有面积概念
没有面积概念
有3个房间

\color{gray}{欢迎大佬儿来指正纠错,共同学习😏!!}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容