1、協(xié)議:定義一個規(guī)則去實現(xiàn)特定的功能,類荠卷、結構體、枚舉都可以遵守這樣的協(xié)議烛愧,并為這個協(xié)議的規(guī)則提供具體實現(xiàn)
protocol SomeProtocol1 {//協(xié)議語法
//協(xié)議內(nèi)容
}
struct SomeStructure: SomeProtocol1 {//遵守協(xié)議油宜,冒號(:)后面加協(xié)議名稱,多個協(xié)議之間用逗號隔開
//結構體內(nèi)容
}
class SomeClass: NSObject,SomeProtocol1 {//有父類的類遵守協(xié)議怜姿,要將父類名放在協(xié)議名之前慎冤,用逗號隔開
//類的實現(xiàn)
}
2、在協(xié)議中定義屬性:協(xié)議中的屬性可以是實例屬性也可以是類型屬性沧卢,協(xié)議中的屬性只能指定名稱和類型以及可讀可寫
protocol SomeProtocol2 {
var mustBeSettable: Int{ get set}//類型后面加{ get set }表示該屬性可讀可寫
var onlyRead: Int{ get } //類型后面加{ get }表示該屬性可讀
static var someTypeProperty: Int { get set }//類型屬性前面加關鍵字static
}
protocol FullyNamed { // 這個協(xié)議中只包含一個實例屬性
var fullName: String { get }
}
struct Person: FullyNamed { // Person遵守FullyNamed協(xié)議表示必需要實現(xiàn)fullName屬性
var name: String
var fullName: String { // 這個fullName屬性可以實現(xiàn)為只讀的
return "Barack Hussein (name)"
}
}
let obama = Person(name: "Obama")
print(obama.fullName) // Barack Hussein Obama
3蚁堤、在協(xié)議中定義方法:協(xié)議可以要求實現(xiàn)指定的實例方法和類方法,定義的方式和普通方法相同搏恤,但不需要大括號和方法體
protocol SomeProtocol3 {
static func someTypeMethod() // 定義類方法的時候用static作前綴
}
protocol RandomNum { // 要求遵守協(xié)議的類型必須有一個名為random的方法
func random() -> Int
}
class RandomNumGenerator: RandomNum{
func random() -> Int {
return Int(arc4random() % 10)
}
}
let randomNum = RandomNumGenerator()
print(randomNum.random()) // 0~9的隨機數(shù)
4违寿、Mutating關鍵字在協(xié)議中的應用:在結構體和枚舉即值類型的實例方法中湃交,不能直接修改其實例屬性,需要在其方法前面加Mutating關鍵字
protocol toggleProtocol {
mutating func toggle() // 對于需要結構體和枚舉遵守的協(xié)議方法需要在前面添加mutating
}
enum Toggle: toggleProtocol {
case Off, On
mutating func toggle() {
switch self {
case .Off:
self = .On
case .On:
self = .Off
}
}
}
var lightSwitch = Toggle.Off
lightSwitch.toggle() // 置反
print(lightSwitch == .On) // true
5藤巢、在協(xié)議中定義構造器:寫下構造器的聲明搞莺,但不需要寫花括號和結構器實體
protocol SomeProtocol4 {
init(someParameter: Int)
}
class SomeInitClass: SomeProtocol4 {
// 遵守協(xié)議的構造器都必須在前面帶required修飾符,來確保所有子類都要實現(xiàn)此構造器
required init(someParameter: Int) {
// 構造器實現(xiàn)部分
}
}
6、協(xié)議作為類型使用:可以作為函數(shù)方法和構造器中的參數(shù)類型或返回值類型掂咒,作為常量變量或?qū)傩缘念愋筒挪祝鳛閿?shù)組字典或其他容器中元素的類型
class Dice { // 定義一個骰子
let generator: RandomNum // 協(xié)議類型的存儲屬性
init(generator: RandomNum) {
self.generator = generator
}
func roll() -> Int { // 產(chǎn)生一個隨機數(shù)
return generator.random()
}
}
class RandomNumGenerator1: RandomNum{ // 定義一個類遵守該協(xié)議
func random() -> Int {
return Int(arc4random() % 10)
}
}
var d6 = Dice(generator: RandomNumGenerator1()) // 就可以將遵守該協(xié)議的類當作參數(shù)了
print(d6.roll()) // 隨機數(shù)
7、代理設計模式:可以將類或結構體的一些功能委托給其他類型去實現(xiàn)绍刮,代理可以用來響應事件或接收外部數(shù)據(jù)源數(shù)據(jù)
class Baby {
var needNumFood: Int? // baby需要的食物數(shù)量
var babyDelegate: BabyDelegate? // 代理屬性
func eat() { // 吃這個方法
babyDelegate?.feedBaby(baby: self) // 調(diào)用代理方法
}
}
class Nanny: BabyDelegate{ // nanny遵守代理
func feedBaby(baby: Baby) { // nanny實現(xiàn)喂食物的代理方法
baby.needNumFood = 10
print("喂baby食物:(baby.needNumFood!)") // 喂baby食物:10
}
}
let baby = Baby()
let nanny = Nanny()
baby.babyDelegate = nanny // 將baby委托給nanny
baby.eat() // baby調(diào)用吃的方法委托nanny喂食物
8温圆、在extention中實現(xiàn)協(xié)議
protocol SomeProtocol5 {
// 協(xié)議內(nèi)容
}
extension Nanny: SomeProtocol5 { // 在擴展中遵守協(xié)議的效果和在原始類中一樣
// 在實際開發(fā)中實現(xiàn)協(xié)議的時候推薦這樣做,有利于提高代碼的閱讀性
}
9、通過擴展遵守協(xié)議:當一個類實現(xiàn)了協(xié)議中的方法孩革,卻還沒有遵守該協(xié)議時岁歉,可以通過空擴展體來遵守該協(xié)議
protocol SomeProtocol6 {
var description: String { get }
}
struct Cat { // 并沒有遵守協(xié)議
var name: String
var description: String { // 實現(xiàn)協(xié)議中的方法
return "A cat named: (name)"
}
}
extension Cat: SomeProtocol6 {} // 在擴展中實現(xiàn)協(xié)議
let lucyTheCat = Cat(name: "lucy")
let sp: SomeProtocol6 = lucyTheCat // 遵守協(xié)議
print(sp.description) // A cat named: lucy
10、協(xié)議本身也是類型膝蜈,可以放到集合中使用
let things: [SomeProtocol6] = [lucyTheCat] // 用于存放遵守協(xié)議的類
for thing in things {
print(thing.description) // A cat named: lucy
}
11锅移、協(xié)議的繼承:和類的繼承相似,但協(xié)議可以繼承一個或多個其它協(xié)議
protocol InheritingProtocol: SomeProtocol5, SomeProtocol6 {
// 任何實現(xiàn)InheritingProtocol協(xié)議的同時,也必須實現(xiàn)SomeProtocol5和SomeProtocol6
}
12饱搏、 類的專屬協(xié)議:通過添加class關鍵字來限制協(xié)議只能被類遵守
protocol SomeClassOnlyProtocol: class, InheritingProtocol { // class關鍵字必須出現(xiàn)在最前面
// 如果被結構體或枚舉繼承則會導致編譯錯誤
}
13非剃、協(xié)議合成:同時采納多個協(xié)議,多個協(xié)議之間用&分割推沸,協(xié)議的合成并不會生成新的協(xié)議類型备绽,只是一個臨時局部的
protocol Name {
var name: String { get }
}
protocol Age {
var age: Int { get }
}
struct People: Name, Age { // 遵守name age這兩個協(xié)議
var name: String
var age: Int
}
func say(to people: Name & Age) { // 參數(shù)類型:Name & Age
print("This is (people.name), age is (people.age)") // This is Joan, age is 20
}
let p = People(name: "Joan", age: 20)
say(to: p)
14、檢查協(xié)議的一致性鬓催,如果不一致可以進行轉(zhuǎn)換
// is 檢查實例是否符合某個協(xié)議,符合返回true,否則返回false
// as? 如果符合某個協(xié)議類型,返回類型為協(xié)議類型的可選值, 否則返回nil
// as! 將實例強制轉(zhuǎn)化為某個協(xié)議類型,如果失敗會引發(fā)運行時錯誤
protocol HasArea { // HasArea協(xié)議
var area: Double { get }
}
class Circle: HasArea { // 遵守HasArea協(xié)議
let pi = 3.1415927
var radius: Double
var area: Double { return pi * radius * radius}
init(radius: Double) { self.radius = radius }
}
class Country: HasArea { // 遵守HasArea協(xié)議
var area: Double
init(area: Double) { self.area = area }
}
class Animal {
var legs: Int
init(legs: Int) { self.legs = legs }
}
15肺素、將所有類對象作為AnyObject對象放到數(shù)組中
let objects: [AnyObject] = [ Circle(radius: 3.0), Country(area: 23460), Animal(legs: 4)]
for object in objects {
if let objectWithArea = object as? HasArea { // 判斷object是否遵守area協(xié)議
print(objectWithArea.area) // 此時的objectWithArea是area協(xié)議類型的實例
print(objectWithArea.pi) // ?, 所以只有area屬性才能被訪問
}else {
print("沒有遵守area協(xié)議")
}
}
16、協(xié)議的可選要求:協(xié)議中所有的方法深浮,屬性并不都是一定要實現(xiàn)的压怠,可以在實現(xiàn)和不實現(xiàn)的方法 面前加optional關鍵字,使用可選方法或?qū)傩詴r飞苇,他們的類型會自動變?yōu)榭蛇x的
// 注意: 可選的協(xié)議前面需要加@objc關鍵字.
// @objc:表示該協(xié)議暴露給OC代碼,但即使不與OC交互只想實現(xiàn)可選協(xié)議要求,還是要加@objc關鍵字.
// 帶有@objc關鍵字的協(xié)議只能被OC類,或者帶有@objc關鍵字的類遵守,結構體和枚舉都不能遵守.
@objc protocol CounterDataSource { // 用于計數(shù)的數(shù)據(jù)源
@objc optional var fixAdd: Int { get } // 可選屬性
@objc optional func addForCount(count: Int) -> Int // 可選方法,用于增加數(shù)值
}
class Counter: CounterDataSource {
var count = 0 // 用來存儲當前值
var dataSource: CounterDataSource?
func add() { // 增加count值
// 使用可選綁定和兩層可選鏈式調(diào)用來調(diào)用可選方法
if let amount = dataSource?.addForCount?(count: count) {
count += amount
}else if let amount = dataSource?.fixAdd {
count += amount
}
}
}
class ThreeSource: NSObject, CounterDataSource {
let fixAdd = 3
}
var counter = Counter()
counter.dataSource = ThreeSource() // 將counter的數(shù)據(jù)源設置為ThreeSource
counter.add() // 增加3
counter.add() // 增加3
print(counter.count) // 6
17菌瘫、協(xié)議的擴展:可以通過擴展協(xié)議來遵守協(xié)議的類型提供屬性方法下標
protocol RandomNumG {
func random() -> Int
}
class RandomNumGen: RandomNumG {
var description: String {
return "RandomNumGen"
}
func random() -> Int {
return Int(arc4random() % 10) // 返回一個0~9的隨機數(shù)
}
}
let randomNumG = RandomNumGen()
print(randomNumG.random()) // 0~9的隨機數(shù)
extension RandomNumG {
var description: String {
return "extension"
}
func randomBool() -> Bool { // 可以通過擴展來為協(xié)議添加方法
return random() > 4 // 隨機數(shù)是否大于4
}
}
print(randomNumG.randomBool()) // bool值
print(randomNumG.description) // RandomNumGen,協(xié)議擴展中的默認屬性的優(yōu)先級比自定義屬性低