? ? ? ?代理又稱為協(xié)議捏顺,委托六孵,protocol ,是iOS開發(fā)中經(jīng)常用到的設(shè)計(jì)模式幅骄。剛學(xué)習(xí)iOS的時(shí)候不是特別理解代理的用法劫窒,估計(jì)是隨著工作中經(jīng)常用,消息傳遞昌执,頁面?zhèn)髦档戎蛞啵?jīng)常用到代理模式诈泼,慢慢對(duì)代理有了點(diǎn)自己的理解懂拾。
OC中的代理模式使用簡(jiǎn)單總結(jié):
1. 定義一個(gè)協(xié)議? 定義遵守協(xié)議的類要實(shí)現(xiàn)的方法
@protocolprotocol name
methods
@end
2. 聲明一個(gè)代理屬性
@property(nonatomic,weak)id delegate;?
3.在另一個(gè)類中遵循協(xié)議
4.給有代理屬性的類設(shè)置代理,即遵循代理的铐达,一般是self
5.遵循代理的類實(shí)現(xiàn)代理方法
我是最近才開始學(xué)swift的岖赋,學(xué)swift 的protocol是對(duì)照這OC學(xué)習(xí)的。swift 中的代理和OC中代理用法差不多瓮孙,用法更加廣泛唐断,更加簡(jiǎn)潔一些。今天看一個(gè)swift開源項(xiàng)目高仿最美應(yīng)用高仿最美應(yīng)用杭抠,類似QQ的側(cè)滑菜單的功能脸甘,中間大量使用了代理來傳遞消息。比如點(diǎn)擊首頁頂部的btn事件偏灿,需要btn去響應(yīng)首頁測(cè)滑的事件丹诀,為了解藕,btn把事件傳給頂部視圖的代理翁垂,頂部視圖的代理傳給首頁VC铆遭,首頁VC代理傳給RootVC去做了。
項(xiàng)目中側(cè)滑菜單實(shí)現(xiàn)原理:主要使用了UINavigationController.interactivePopGestureRecognizer這個(gè)pop手勢(shì)沿猜。在rootVC中添加兩個(gè)VC枚荣,leftVC,RightVC啼肩,定義一個(gè)紀(jì)錄當(dāng)前VC的currentVC橄妆,并且將兩個(gè)vc的View添加到self.view,在currentVC上再添加一個(gè)覆蓋window衙伶,在window上添加手勢(shì)動(dòng)作。
結(jié)合項(xiàng)目學(xué)習(xí)會(huì)不那么枯燥害碾。
下面是我的swift protocol學(xué)習(xí)筆記
protocol SomeProtocol {
//這里是協(xié)議的定義部分
}
? ? ? ?讓自定義類型遵循某個(gè)協(xié)議痕支,在類型名稱后加上協(xié)議名稱,中間以冒號(hào)(:)分隔蛮原。遵循多個(gè)協(xié)議時(shí)卧须,各協(xié)議之間用逗號(hào)(,)分隔
struct SomeStructre:FirstProtocol, AnotherProtocol {
//這里時(shí)結(jié)構(gòu)體的定義部分
}
擁有父類的類在遵循協(xié)議時(shí)儒陨,應(yīng)該將父類名放在協(xié)議名之前花嘶,以逗號(hào)分隔
class someClass:SomeSuperClass, FirstProtocol,AnotherProtocol {
// 這里是類的定義部分
}
屬性要求
協(xié)議可以要求遵循協(xié)議的類型提供特定名稱和類型的實(shí)例屬性。協(xié)議不指定屬性是存儲(chǔ)型屬性還是計(jì)算屬性蹦漠,它只是制定屬性的名稱和類型椭员。
protocol SomeProtocol {
var mustBeSettable:Int {get set}
var doesNotNeedToBeSettable:Int {get}
}
在協(xié)議中定義屬性時(shí),總是使用static關(guān)鍵字作為前綴笛园。當(dāng)類型遵循協(xié)議時(shí)隘击,除了static關(guān)鍵字,還可以使用class關(guān)鍵字來聲明類型屬性
protocol AnotherProtocol {
static var someTypeProoerty:Int {get set}
}
protocol FullyNamed {
var fullName:String {get}
}
// FullyNamed協(xié)議除了要求遵循協(xié)議的類型提供fullName屬性外研铆,并沒有其他特別的要求埋同。這個(gè)協(xié)議表示,在任何遵循FullyNamed的類型棵红,都必須有一個(gè)可讀的String類型的實(shí)例屬性fullName凶赁。
struct Person:FullyNamed {
var? fullName: String
}
let john = Person(fullName: "john Appleseed")
// Person 結(jié)構(gòu)體的每一個(gè)實(shí)例都有一個(gè)String類型的存儲(chǔ)類型屬性fullName。這正好滿足了FullyNamed協(xié)議的要求逆甜,也就意味著Person結(jié)構(gòu)體正確地附和了協(xié)議虱肄。
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
}
}
// 方法要求
// 協(xié)議可以要求遵循協(xié)議的類型實(shí)現(xiàn)某些指定的實(shí)例方法或者類方法
protocol RangedomNumberGenerator {
func random() -> Double
}
class LinearCongtuentialGenerator:RangedomNumberGenerator {
var lastRandom = 42.0
let m = 139968.0
let a = 3877.0
let c = 29573.0
// 實(shí)現(xiàn)RangedomNumberGenerator協(xié)議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初始化兩個(gè)參數(shù) sides generator是一個(gè)遵循了RangedomNumberGenerator協(xié)議的類的實(shí)例
var d6 = Dice(sides: 6, generator: LinearCongtuentialGenerator())
for _ in 1...5 {
print("random dice roll is \(d6.roll())")
}
// 委托代理模式
// 委托是一種設(shè)計(jì)模式,它允許類或者結(jié)構(gòu)體將一些需要它們負(fù)責(zé)的功能委托給其他類型的實(shí)例交煞。委托模式實(shí)現(xiàn)很簡(jiǎn)單:定義協(xié)議來封裝那些需要被委托的功能咏窿,這樣就能確保遵循協(xié)議的類型能提供這些功能。委托模式可以用來響應(yīng)特定的動(dòng)作素征,或者接收外部數(shù)據(jù)源提供的數(shù)據(jù)集嵌,而無需關(guān)心外部數(shù)據(jù)源的類型
protocol Dicegame {
var dice:Dice {get}
// 定義Dicegame協(xié)議方法play()
func play()
}
protocol DicegameDelegate {
// Dicegame 作為類型
func gameDidStart(_ game:Dicegame)
func game(_ game:Dicegame, didStartNewTurnWithDiceRoll diceRoll:Int)
func gameDidEnd(_ game:Dicegame)
}
// 遵循Dicegame協(xié)議
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?
// 實(shí)現(xiàn) Dicegame協(xié)議 的play()方法
func play() {
square = 0
// 調(diào)用DicegameDelegate的代理方法? 參數(shù)是Dicegame類型的實(shí)例
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)
}
}
// 這個(gè)版本的游戲封裝到了 SnakesAndLadders 類中,該類遵循了 Dicegame 協(xié)議稚茅,并且提供了相應(yīng)的可讀的dice屬性和play()方法
// 游戲使用SnakesAndLadders類init()構(gòu)造器來初始化游戲.所有的游戲邏輯被轉(zhuǎn)移到了協(xié)議中的play()方法纸淮,play()方法使用協(xié)議要求的dice屬性提供骰子搖出的值。
// 注意亚享,delegate并不是游戲的必備條件咽块,因此delegate被定義DicegameDelegate類型的可選屬性。因?yàn)閐elegate是可選值欺税,因此會(huì)被自動(dòng)賦予初始值nill侈沪。隨后揭璃,可以在游戲中為delegate設(shè)置適當(dāng)?shù)闹怠?/p>
// DicegameDelegate協(xié)議提供了三個(gè)方法用來追蹤游戲的過程。這三個(gè)方法被放置在play方法內(nèi)亭罪,分別在游戲開始時(shí)瘦馍,新一輪開始時(shí),以及游戲結(jié)束時(shí)被調(diào)用
// 因?yàn)閐elegate是一個(gè)DicegameDelegate類型的可選屬性应役,因此在play()方法中通過可選鏈?zhǔn)秸{(diào)用來調(diào)用它的方法情组。若delegate屬性為nill,則調(diào)用方法會(huì)優(yōu)雅地失敗箩祥,并不會(huì)產(chǎn)生錯(cuò)誤院崇,若delegate不為nill,則方法能夠被調(diào)用袍祖,并傳遞SnakesAndLadders實(shí)例為參數(shù)
// DiceGameTracker類遵循DicegameDelegate協(xié)議
class DiceGameTracker:DicegameDelegate {
var numberOfTurns = 0
// 實(shí)現(xiàn)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協(xié)議 實(shí)例
let tracker = DiceGameTracker()
// 遵循Dicegame協(xié)議 的實(shí)例
let game = SnakesAndLadders()
// 設(shè)置game的代理為 tracker
game.delegate = tracker
//? 調(diào)用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)
// 通過擴(kuò)展遵循協(xié)議
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)
// 協(xié)議類型的集合
let things:[TextRepresentable] = [game, dl2, simonTheHamster]
for thing in things {
print(thing.textualDescription)
}
// 協(xié)議的繼承
// 協(xié)議能夠繼承一個(gè)或者多個(gè)其他協(xié)議底瓣,可以在繼承的協(xié)議的基礎(chǔ)上增加新的要求。協(xié)議的繼承語法和類的繼承相似蕉陋,多個(gè)被繼承的協(xié)議間用逗號(hào)分隔
protocol InhertingProtocol:SomeProtocol, AnotherProtocol {
// 協(xié)議的定義部分
}
// 定義一個(gè)新的協(xié)議PrettyTextRepresentable 捐凭,它繼承自TextRepresentable 任何遵循PrettyTextRepresentable協(xié)議的類型在滿足該協(xié)議的要求時(shí),也必須滿足TextRepresentable協(xié)議的要求
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)
//類類型專屬協(xié)議
// 你可以在協(xié)議的繼承列表中凳鬓,通過添加class關(guān)鍵字來限制協(xié)議只能夠被類類型遵循茁肠,而結(jié)構(gòu)體和枚舉不能遵循該協(xié)議。class關(guān)鍵字必須第一個(gè)出現(xiàn)在協(xié)議的繼承列表中村视,在其他繼承的協(xié)議之前
protocol SomeClassOnlyProtocol:class, InhertingProtocol {
// 這里是類類型專屬協(xié)議的定義部分
}
// 協(xié)議合成
// 有時(shí)候需要同時(shí)遵循多個(gè)協(xié)議官套,你可以將多個(gè)協(xié)議采用SomeProtocol&AnotherProtocol這樣的格式進(jìn)行組合,稱為協(xié)議合成蚁孔。你可以羅列任意多個(gè)你想要遵循的協(xié)議,以符號(hào)&分隔
protocol Named {
var name:String {get}
}
protocol Aged {
var age:Int {get}
}
struct Person1: Named , Aged {
var name: String
var age: Int
}
// 遵循Named和Aged兩個(gè)協(xié)議的類型 不關(guān)心參數(shù)的具體類型惋嚎,只要參數(shù)復(fù)合這兩個(gè)協(xié)議即可
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);
// 檢查協(xié)議一致性
// 你可以使用類型轉(zhuǎn)換中描述的is 和as操作符來檢查協(xié)議的一致性杠氢,即是否符合某協(xié)議,并且可以轉(zhuǎn)換到指定的協(xié)議類型另伍。檢查和轉(zhuǎn)換到某個(gè)協(xié)議類型在語法上和類型的檢查和轉(zhuǎn)換完全相同
// is 用來檢查實(shí)例是否符合某個(gè)協(xié)議鼻百,若符合則返回ture,否則返回false
// as? 返回一個(gè)可選值摆尝,當(dāng)實(shí)例符合某個(gè)協(xié)議時(shí)温艇,返回類型為協(xié)議類型的可選值,否則返回nill
// as!將實(shí)例強(qiáng)制向下轉(zhuǎn)換到某個(gè)協(xié)議類型堕汞,如果強(qiáng)轉(zhuǎn)失敗勺爱,會(huì)引發(fā)運(yùn)行時(shí)錯(cuò)誤
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 并沒有一個(gè)共同的基類,盡管如此讯检,它們都是類琐鲁,它們的實(shí)例都可以作為AnyObject類型的值卫旱,存儲(chǔ)在同一個(gè)數(shù)組中
let objects:[AnyObject] = [
Circle(radius: 2.0),
Country(area: 243_610),
Animal(legs: 4)
]
// 檢查數(shù)組中的每一個(gè)元素,看它是否符合HasArea協(xié)議
for object in objects {
// 當(dāng)元素符合HasArea協(xié)議時(shí)围段,將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 官方教程