一、Swift协议

协议是为方法、属性等定义一套规范,没有具体的实现,类似于Java中的抽象接口,它只是描述了方法或属性的骨架,而不是实现。方法和属性实现还需要通过定义类,函数和枚举完成。

1. 协议定义
//协议定义通过关键字protocol
protocol SomeProtocol {
//协议定义
}
//协议可以继承一个或者多个协议
protocol SomeProtocol2: SomeProtocol {
//协议定义
}
//结构体实现协议
struct SomeStructure: SomeProtocol, SomeProtocol2 {
//结构体定义
}
//类实现协议和继承父类,协议一般都写在父类后面
class SomeSuperclass {
//父类定义
}
class SomeClass: SomeSuperclass, SomeProtocol, SomeProtocol2 {
//子类定义
}
2. 属性要求

协议不指定是否该属性应该是一个存储属性或者计算属性,它只指定所需的属性名称和读写类型。属性要求总是声明为变量属性,用var关键字做前缀。

protocol ClassProtocol {
static var present: Bool { get set } //要求该属性可读可写,并且是静态的
var subject: String { get } //要求该属性可读
var stname: String { get set } //要求该属性可读可写
}
//定义类来实现协议
class MyClass: ClassProtocol {
static var present = false //如果没有实现协议的属性要求,会直接报错
var subject = "Swift Protocols" //该属性设置为可读可写,也是满足协议要求的
var stname = "Class"
func attendance() -> String {
return "The \(self.stname) has secured 99% attendance"
}
func markssecured() -> String {
return "\(self.stname) has \(self.subject)"
}
}
//创建对象
var classa = MyClass()
print(classa.attendance()) //结果:The Class has secured 99% attendance
print(classa.markssecured()) //结果:Class has Swift Protocols
3. 普通实例方法要求

协议可以要求指定实例方法和类型方法被一致的类型实现。这些方法被写为协议定义的一部分,跟普通实例和类型方法完全一样,但是没有大括号或方法体。可变参数是允许的,普通方法也遵循同样的规则,不过不允许给协议方法参数指定默认值。

//定义协议,指定方法要求
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) % m)
return lastRandom / m
}
}
let generator = LinearCongruentialGenerator()
print("随机数: \(generator.random())") //结果:随机数: 0.37464991998171
print("另一个随机数: \(generator.random())") //结果:另一个随机数: 0.729023776863283
4. Mutating方法要求

有时需要一个方法来修改它属于的实例。对值类型实例方法(即结构和枚举),你将mutating关键字放在方法func关键字之前,表明该方法允许修改所属实例的任何属性。这个过程描述在实例方法内修改值类型,通常用于结构体和枚举。

protocol Togglable {
//协议的Mutating方法要求,允许在该方法内修改值类型
mutating func toggle()
}
//定义枚举实现协议
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变成了OnOffSwitch.On
switch(lightSwitch) {
case .On:
print("开关On")
case .Off:
print("开关Off")
}
//打印:开关On
5. 初始化构造器要求

协议SomeProtocol中不光可以声明方法/属性/下标,还可以声明构造器,但在Swift中,除了某些特殊情况外,构造器是不被子类继承的,所以SomeClass中虽然能够保证定义了协议要求的构造器,但不能保证SomeClass的子类中也定义了协议要求的构造器。所以我们需要在实现协议要求的构造器时,使用required关键字确保SomeClass的子类必须也得实现这个构造器。

protocol TcpProtocol {
//初始化构造器要求
init(aprot: Int)
}
class TcpClass: TcpProtocol {
var aprot: Int
//实现协议的初始化要求时,必须使用required关键字确保子类必须也得实现这个构造器
required init(aprot: Int) {
self.aprot = aprot
}
}
var tcp = TcpClass(aprot: 20)
print(tcp.aprot)
6. 协议类型使用
协议可以作为类型访问:
  • 函数,方法或初始化作为一个参数或返回类型
  • 常量,变量或属性
  • 数组,字典或其他容器作为项目
//定义随机数生成器协议
protocol RandomNumberGenerator {
func random() -> Double
}
//实现RandomNumberGenerator协议的类
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) % m)
return lastRandom / m
}
}
//定义骰子类
class Dice {
let sides: Int //表示「骰子」有几个面
let generator: RandomNumberGenerator //随机数生成器 //指定构造器,RandomNumberGenerator是一个协议名
init(sides: Int, generator: RandomNumberGenerator) {
self.sides = sides
self.generator = generator
} //摇动「骰子」
func roll() -> Int {
return Int(generator.random() * Double(sides)) + 1
}
}
//创建一个6面骰子
var dice6 = Dice(sides: 6, generator: LinearCongruentialGenerator())
for i in 1...5 {
print("摇动骰子:\(dice6.roll())")
}
/*
摇动骰子:3
摇动骰子:5
摇动骰子:4
摇动骰子:5
摇动骰子:4
*/
7. 协议组合

协议组合对于要求一个类型立即符合多种协议是有用的。

protocol Named {
var name: String { get }
}
protocol Aged {
var age: Int { get }
}
//定义结构体实现上面2个协议
struct Person: Named, Aged {
var name: String
var age: Int
}
//定义一个函数,接受一个符合Named和Aged协议的类型
func wishHappyBirthday(celebrator: protocol<Named, Aged>) {
print("\(celebrator.name)\(celebrator.age)岁生日快乐!")
}
//创建一个Person结构体,实现了Named和Aged协议
let birthdayPerson = Person(name: "小明", age: 21)
wishHappyBirthday(birthdayPerson) //结果:小明21岁生日快乐!
8. 可选实现要求

OC中协议定义的属性和变量有requiredoptionalSwift中你可以为协议定义optional要求,这些要求不需要被符合协议的类型实现。

  1. Optional协议要求只有在你的协议被@objc属性标记时指定。
  2. 即使你不与Objective-C交互,如果你希望指定optional要求,你仍然需要使用@objc标记你的协议。
  3. 使用@objc标记的协议只能通过类调用

根据我的理解,Swift的设计理念是没有可选的协议实现概念,但是为了保持与OC兼容性,不得已支持;所以在Swift的协议中定义可选实现的前提是该协议被@objc修饰,关于@objc

  1. @objc指示该协议暴露给OC,即可以为OC代码所用
  2. @objc修饰的协议仅仅可以被类class类型遵循
@objc protocol CounterDataSource {
//协议可选实现的方法要求
@optional func incrementForCount(count: Int) -> Int
//协议可选实现的属性要求
@optional var fixedIncrement: Int { get }
}
@objc class Counter {
var count = 0
var dataSource: CounterDataSource? //数据源属性,可选类型
func increment() {
//判断是否数据源有,数据源是否有实现可选的方法和属性
if let amount = dataSource?.incrementForCount?(count) {
count += amount
} else if let amount = dataSource?.fixedIncrement? {
count += amount
}
}
}
@objc class ThreeSource: CounterDataSource {
let fixedIncrement = 3
}
var counter = Counter()
counter.dataSource = ThreeSource() //设置数据源
for i in 1...4 {
counter.increment()
print("\(counter.count) ") //打印:3 6 9 12
}
  1. 由于dataSource可能为nil,因此在dataSource后边加上了?标记来表明只在dataSource非空时才去调用incrementForCount方法。
  2. 即使dataSource存在,但是也无法保证其是否实现了incrementForCount方法,因此在incrementForCount方法后边也加有?标记。

iOS学习笔记45-Swift(五)协议的更多相关文章

  1. iOS学习笔记(十五)——数据库操作(SQLite)

    SQLite (http://www.sqlite.org/docs.html) 是一个轻量级的关系数据库.SQLite最初的设计目标是用于嵌入式系统,它占用资源非常少,在嵌入式设备中,只需要几百K的 ...

  2. 【转】iOS学习笔记(十五)——数据库操作(SQLite)

    SQLite (http://www.sqlite.org/docs.html) 是一个轻量级的关系数据库.SQLite最初的设计目标是用于嵌入式系统,它占用资源非常少,在嵌入式设备中,只需要几百K的 ...

  3. iOS学习笔记-精华整理

    iOS学习笔记总结整理 一.内存管理情况 1- autorelease,当用户的代码在持续运行时,自动释放池是不会被销毁的,这段时间内用户可以安全地使用自动释放的对象.当用户的代码运行告一段 落,开始 ...

  4. iOS学习笔记总结整理

    来源:http://mobile.51cto.com/iphone-386851_all.htm 学习IOS开发这对于一个初学者来说,是一件非常挠头的事情.其实学习IOS开发无外乎平时的积累与总结.下 ...

  5. iOS学习笔记-自定义过渡动画

    代码地址如下:http://www.demodashi.com/demo/11678.html 这篇笔记翻译自raywenderlick网站的过渡动画的一篇文章,原文用的swift,由于考虑到swif ...

  6. iOS学习笔记13-网络(二)NSURLSession

    在2013年WWDC上苹果揭开了NSURLSession的面纱,将它作为NSURLConnection的继任者.现在使用最广泛的第三方网络框架:AFNetworking.SDWebImage等等都使用 ...

  7. IOS学习笔记之关键词@dynamic

    IOS学习笔记之关键词@dynamic @dynamic这个关键词,通常是用不到的. 它与@synthesize的区别在于: 使用@synthesize编译器会确实的产生getter和setter方法 ...

  8. iOS学习笔记之Category

    iOS学习笔记之Category 写在前面 Category是类别(也称为类目或范畴),使用Category,程序员可以为任何已有的类添加方法.使用类别可以对框架提供的类(无法获取源码,不能直接修改) ...

  9. iOS学习笔记之ARC内存管理

    iOS学习笔记之ARC内存管理 写在前面 ARC(Automatic Reference Counting),自动引用计数,是iOS中采用的一种内存管理方式. 指针变量与对象所有权 指针变量暗含了对其 ...

  10. iOS学习笔记之UITableViewController&UITableView

    iOS学习笔记之UITableViewController&UITableView 写在前面 上个月末到现在一直都在忙实验室的事情,与导师讨论之后,发现目前在实验室完成的工作还不足以写成毕业论 ...

随机推荐

  1. Android(java)学习笔记123:Android MediaPlayer 播放prepareAsync called in state 8解决办法

    1. 使用android MediaPlayer播放音频文件时,有时会出现prepareasync called in state 8错误. 以下方法可以避免这个异常出现.  第1种方法: priva ...

  2. tpcc-mysql 实践

    一.TPCC 介绍 TPC:全称Transaction Processing Performance Council (事务处理性能委员会),是一家非盈利性组织,该组织制定各种商业应用的基准测试规范, ...

  3. Python封装补充

    property属性 property实际是setter getter deleter是集合体,并不是一个单独的方法 import math # 使用的库 class Circle: def __in ...

  4. c++结构体双关键字排序

    #include<bits/stdc++.h> using namespace std; struct node{ int l,r; }num[]; int w_comp(const no ...

  5. SAP行列转换的一种方法

    一段经典的代码写在这里 TABLES spfli. DATA: lt_data TYPE STANDARD TABLE OF spfli, lwa_ref TYPE REF TO data, lt_f ...

  6. MyBatis逆向工程中的Mapper接口以及Example的实例函数及详解

    一.mapper接口中的方法解析 mapper接口中的函数及方法 方法 功能说明 int countByExample(UserExample example) thorws SQLException ...

  7. windows charles 抓包https请求

    charles证书     2.设置host和端口 3.浏览器访问即可抓到https的请求  

  8. STA basic

  9. Survey lists 10 most innovative cities

    From China Daily Beijing and Shanghai are among the 10 most innovative cities in the world, based on ...

  10. Python入门基础--字符编码与文件处理

    字符编码 文本编辑器存取文件的原理 #1.打开编辑器就打开了启动了一个进程,是在内存中的,所以,用编辑器编写的内容也都是存放与内存中的,断电后数据丢失 #2.要想永久保存,需要点击保存按钮:编辑器把内 ...