swift 的protocol学习笔记
代理又称为协议,委托,protocol ,是iOS开发中经常用到的设计模式。刚学习iOS的时候不是特别理解代理的用法,估计是随着工作中经常用,消息传递,页面传值等,经常用到代理模式,慢慢对代理有了点自己的理解。
OC中的代理模式使用简单总结:
1. 定义一个协议 定义遵守协议的类要实现的方法
@protocolprotocol name
methods
@end
2. 声明一个代理属性
@property(nonatomic,weak)id delegate;
3.在另一个类中遵循协议
4.给有代理属性的类设置代理,即遵循代理的,一般是self
5.遵循代理的类实现代理方法
我是最近才开始学swift的,学swift 的protocol是对照这OC学习的。swift 中的代理和OC中代理用法差不多,用法更加广泛,更加简洁一些。今天看一个swift开源项目高仿最美应用高仿最美应用,类似QQ的侧滑菜单的功能,中间大量使用了代理来传递消息。比如点击首页顶部的btn事件,需要btn去响应首页测滑的事件,为了解藕,btn把事件传给顶部视图的代理,顶部视图的代理传给首页VC,首页VC代理传给RootVC去做了。
项目中侧滑菜单实现原理:主要使用了UINavigationController.interactivePopGestureRecognizer这个pop手势。在rootVC中添加两个VC,leftVC,RightVC,定义一个纪录当前VC的currentVC,并且将两个vc的View添加到self.view,在currentVC上再添加一个覆盖window,在window上添加手势动作。
结合项目学习会不那么枯燥。
下面是我的swift protocol学习笔记
protocol SomeProtocol {
//这里是协议的定义部分
}
让自定义类型遵循某个协议,在类型名称后加上协议名称,中间以冒号(:)分隔。遵循多个协议时,各协议之间用逗号(,)分隔
struct SomeStructre:FirstProtocol, AnotherProtocol {
//这里时结构体的定义部分
}
拥有父类的类在遵循协议时,应该将父类名放在协议名之前,以逗号分隔
class someClass:SomeSuperClass, FirstProtocol,AnotherProtocol {
// 这里是类的定义部分
}
属性要求
协议可以要求遵循协议的类型提供特定名称和类型的实例属性。协议不指定属性是存储型属性还是计算属性,它只是制定属性的名称和类型。
protocol SomeProtocol {
var mustBeSettable:Int {get set}
var doesNotNeedToBeSettable:Int {get}
}
在协议中定义属性时,总是使用static关键字作为前缀。当类型遵循协议时,除了static关键字,还可以使用class关键字来声明类型属性
protocol AnotherProtocol {
static var someTypeProoerty:Int {get set}
}
protocol FullyNamed {
var fullName:String {get}
}
// FullyNamed协议除了要求遵循协议的类型提供fullName属性外,并没有其他特别的要求。这个协议表示,在任何遵循FullyNamed的类型,都必须有一个可读的String类型的实例属性fullName。
struct Person:FullyNamed {
var fullName: String
}
let john = Person(fullName: "john Appleseed")
// Person 结构体的每一个实例都有一个String类型的存储类型属性fullName。这正好满足了FullyNamed协议的要求,也就意味着Person结构体正确地附和了协议。
class Startship:FullyNamed {
var prefix:String?
var name:String
init(name:String, prefix:String?=nil) {
self.name = name
self.prefix = prefix
}
var fullName: String {
return (prefix != nil ? prefix! + " ":"") + name
}
}
// 方法要求
// 协议可以要求遵循协议的类型实现某些指定的实例方法或者类方法
protocol RangedomNumberGenerator {
func random() -> Double
}
class LinearCongtuentialGenerator:RangedomNumberGenerator {
var lastRandom = 42.0
let m = 139968.0
let a = 3877.0
let c = 29573.0
// 实现RangedomNumberGenerator协议random方法
func random() -> Double {
lastRandom = (lastRandom * a + c).truncatingRemainder(dividingBy: m)
return lastRandom / m
}
}
let generator = LinearCongtuentialGenerator()
generator.random()
(generator.random())
protocol Togglable {
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()
// 作为类型
class Dice {
let sides:Int
let generator:RangedomNumberGenerator
init(sides:Int, generator:RangedomNumberGenerator) {
self.sides = sides
self.generator = generator
}
func roll() -> Int {
return Int(generator.random() * Double(sides))
}
}
// Dice初始化两个参数 sides generator是一个遵循了RangedomNumberGenerator协议的类的实例
var d6 = Dice(sides: 6, generator: LinearCongtuentialGenerator())
for _ in 1...5 {
print("random dice roll is \(d6.roll())")
}
// 委托代理模式
// 委托是一种设计模式,它允许类或者结构体将一些需要它们负责的功能委托给其他类型的实例。委托模式实现很简单:定义协议来封装那些需要被委托的功能,这样就能确保遵循协议的类型能提供这些功能。委托模式可以用来响应特定的动作,或者接收外部数据源提供的数据,而无需关心外部数据源的类型
protocol Dicegame {
var dice:Dice {get}
// 定义Dicegame协议方法play()
func play()
}
protocol DicegameDelegate {
// Dicegame 作为类型
func gameDidStart(_ game:Dicegame)
func game(_ game:Dicegame, didStartNewTurnWithDiceRoll diceRoll:Int)
func gameDidEnd(_ game:Dicegame)
}
// 遵循Dicegame协议
class SnakesAndLadders:Dicegame {
let finalSquare = 25
let dice = Dice(sides: 6, generator: LinearCongtuentialGenerator())
var square = 0
var board:[Int]
init () {
board = [Int](repeating:0, count:finalSquare + 1)
board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
}
// 定义代理
var delegate:DicegameDelegate?
// 实现 Dicegame协议 的play()方法
func play() {
square = 0
// 调用DicegameDelegate的代理方法 参数是Dicegame类型的实例
delegate?.gameDidStart(self)
gameLoop:while square != finalSquare {
let diceRoll = dice.roll()
delegate?.game(self, didStartNewTurnWithDiceRoll:diceRoll)
switch square + diceRoll {
case finalSquare:
break gameLoop
case let newSquare where newSquare > finalSquare:
continue gameLoop
default:
square += diceRoll
square += board[square]
}
}
delegate?.gameDidEnd(self)
}
}
// 这个版本的游戏封装到了 SnakesAndLadders 类中,该类遵循了 Dicegame 协议,并且提供了相应的可读的dice属性和play()方法
// 游戏使用SnakesAndLadders类init()构造器来初始化游戏.所有的游戏逻辑被转移到了协议中的play()方法,play()方法使用协议要求的dice属性提供骰子摇出的值。
// 注意,delegate并不是游戏的必备条件,因此delegate被定义DicegameDelegate类型的可选属性。因为delegate是可选值,因此会被自动赋予初始值nill。随后,可以在游戏中为delegate设置适当的值。
// DicegameDelegate协议提供了三个方法用来追踪游戏的过程。这三个方法被放置在play方法内,分别在游戏开始时,新一轮开始时,以及游戏结束时被调用
// 因为delegate是一个DicegameDelegate类型的可选属性,因此在play()方法中通过可选链式调用来调用它的方法。若delegate属性为nill,则调用方法会优雅地失败,并不会产生错误,若delegate不为nill,则方法能够被调用,并传递SnakesAndLadders实例为参数
// DiceGameTracker类遵循DicegameDelegate协议
class DiceGameTracker:DicegameDelegate {
var numberOfTurns = 0
// 实现DicegameDelegate方法
func gameDidStart(_ game: Dicegame) {
numberOfTurns = 0
if game is SnakesAndLadders {
print("Start a new game of Snakes and Ladders")
}
print("The game is using a \(game.dice.sides)-side dice")
}
func game(_ game: Dicegame, didStartNewTurnWithDiceRoll diceRoll: Int) {
numberOfTurns += 1
print("Rolled a \(diceRoll)")
}
func gameDidEnd(_ game: Dicegame) {
print("The game lasted for \(numberOfTurns) turns")
}
}
// DiceGameTracker类遵循DicegameDelegate协议 实例
let tracker = DiceGameTracker()
// 遵循Dicegame协议 的实例
let game = SnakesAndLadders()
// 设置game的代理为 tracker
game.delegate = tracker
// 调用play方法
game.play()
protocol TextRepresentable {
var textualDescription:String {get}
}
extension Dice:TextRepresentable {
var textualDescription:String {
return "A \(sides)-sided dice"
}
}
let dl2 = Dice(sides: 12, generator: LinearCongtuentialGenerator())
print(dl2.textualDescription)
// 通过扩展遵循协议
extension SnakesAndLadders:TextRepresentable {
var textualDescription:String {
return "A game of Snakes and Ladders with \(finalSquare) squares"
}
}
print(game.textualDescription)
struct Hamster {
var name:String
var textualDescription:String {
return "A hamster named \(name)"
}
}
extension Hamster:TextRepresentable {}
let simonTheHamster = Hamster(name: "Simon")
let somethingTextRepresentable:TextRepresentable = simonTheHamster
print(somethingTextRepresentable.textualDescription)
// 协议类型的集合
let things:[TextRepresentable] = [game, dl2, simonTheHamster]
for thing in things {
print(thing.textualDescription)
}
// 协议的继承
// 协议能够继承一个或者多个其他协议,可以在继承的协议的基础上增加新的要求。协议的继承语法和类的继承相似,多个被继承的协议间用逗号分隔
protocol InhertingProtocol:SomeProtocol, AnotherProtocol {
// 协议的定义部分
}
// 定义一个新的协议PrettyTextRepresentable ,它继承自TextRepresentable 任何遵循PrettyTextRepresentable协议的类型在满足该协议的要求时,也必须满足TextRepresentable协议的要求
protocol PrettyTextRepresentable:TextRepresentable {
var prettyTextualDescription:String {get}
}
extension SnakesAndLadders:PrettyTextRepresentable {
var prettyTextualDescription:String {
var output = textualDescription + ":\n"
for index in 1...finalSquare {
switch board[index] {
case let ladder where ladder > 0:
output += "▶️"
case let snake where snake < 0:
output += "◀️"
default:
output += "⭕️"
}
}
return output
}
}
print(game.prettyTextualDescription)
//类类型专属协议
// 你可以在协议的继承列表中,通过添加class关键字来限制协议只能够被类类型遵循,而结构体和枚举不能遵循该协议。class关键字必须第一个出现在协议的继承列表中,在其他继承的协议之前
protocol SomeClassOnlyProtocol:class, InhertingProtocol {
// 这里是类类型专属协议的定义部分
}
// 协议合成
// 有时候需要同时遵循多个协议,你可以将多个协议采用SomeProtocol&AnotherProtocol这样的格式进行组合,称为协议合成。你可以罗列任意多个你想要遵循的协议,以符号&分隔
protocol Named {
var name:String {get}
}
protocol Aged {
var age:Int {get}
}
struct Person1: Named , Aged {
var name: String
var age: Int
}
// 遵循Named和Aged两个协议的类型 不关心参数的具体类型,只要参数复合这两个协议即可
func wishHappyBirthday(to celebrator:Named & Aged) {
print("Happy birthday,\(celebrator.name), you're \(celebrator.age)!")
}
let birthdayPerson = Person1(name: "Malcolm", age: 21)
wishHappyBirthday(to: birthdayPerson);
// 检查协议一致性
// 你可以使用类型转换中描述的is 和as操作符来检查协议的一致性,即是否符合某协议,并且可以转换到指定的协议类型。检查和转换到某个协议类型在语法上和类型的检查和转换完全相同
// is 用来检查实例是否符合某个协议,若符合则返回ture,否则返回false
// as? 返回一个可选值,当实例符合某个协议时,返回类型为协议类型的可选值,否则返回nill
// 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
}
}
// Circle,Country, Animal 并没有一个共同的基类,尽管如此,它们都是类,它们的实例都可以作为AnyObject类型的值,存储在同一个数组中
let objects:[AnyObject] = [
Circle(radius: 2.0),
Country(area: 243_610),
Animal(legs: 4)
]
// 检查数组中的每一个元素,看它是否符合HasArea协议
for object in objects {
// 当元素符合HasArea协议时,将as?操作符返回的可选值
if let objectWithArea = object as? HasArea {
print("Area is \(objectWithArea.area)")
} else {
print("Something that desen't have an area")
}
}
var ncc1701 = Startship(name: "Enterprise", prefix: "USS") // startship
参考:http://www.cocoachina.com/ios/20160317/15696.html
swift 官方教程