Swift學習筆記十七之協議

1、協議:定義一個規則去實現特定的功能,類、結構體、枚舉都可以遵守這樣的協議,并為這個協議的規則提供具體實現

protocol SomeProtocol1 {//協議語法
//協議內容
}

struct SomeStructure: SomeProtocol1 {//遵守協議,冒號(:)后面加協議名稱,多個協議之間用逗號隔開
//結構體內容
}

class SomeClass: NSObject,SomeProtocol1 {//有父類的類遵守協議,要將父類名放在協議名之前,用逗號隔開
//類的實現
}

2、在協議中定義屬性:協議中的屬性可以是實例屬性也可以是類型屬性,協議中的屬性只能指定名稱和類型以及可讀可寫

protocol SomeProtocol2 {
var mustBeSettable: Int{ get set}//類型后面加{ get set }表示該屬性可讀可寫
var onlyRead: Int{ get } //類型后面加{ get }表示該屬性可讀
static var someTypeProperty: Int { get set }//類型屬性前面加關鍵字static

}

protocol FullyNamed { // 這個協議中只包含一個實例屬性
var fullName: String { get }
}

struct Person: FullyNamed { // Person遵守FullyNamed協議表示必需要實現fullName屬性
var name: String
var fullName: String { // 這個fullName屬性可以實現為只讀的
return "Barack Hussein (name)"
}
}
let obama = Person(name: "Obama")
print(obama.fullName) // Barack Hussein Obama
3、在協議中定義方法:協議可以要求實現指定的實例方法和類方法,定義的方式和普通方法相同,但不需要大括號和方法體

protocol SomeProtocol3 {
static func someTypeMethod() // 定義類方法的時候用static作前綴
}

protocol RandomNum { // 要求遵守協議的類型必須有一個名為random的方法
func random() -> Int
}
class RandomNumGenerator: RandomNum{
func random() -> Int {
return Int(arc4random() % 10)
}
}
let randomNum = RandomNumGenerator()
print(randomNum.random()) // 0~9的隨機數

4、Mutating關鍵字在協議中的應用:在結構體和枚舉即值類型的實例方法中,不能直接修改其實例屬性,需要在其方法前面加Mutating關鍵字

protocol toggleProtocol {
mutating func toggle() // 對于需要結構體和枚舉遵守的協議方法需要在前面添加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、在協議中定義構造器:寫下構造器的聲明,但不需要寫花括號和結構器實體
protocol SomeProtocol4 {
init(someParameter: Int)
}

class SomeInitClass: SomeProtocol4 {
// 遵守協議的構造器都必須在前面帶required修飾符,來確保所有子類都要實現此構造器
required init(someParameter: Int) {
// 構造器實現部分
}
}

6、協議作為類型使用:可以作為函數方法和構造器中的參數類型或返回值類型,作為常量變量或屬性的類型,作為數組字典或其他容器中元素的類型
class Dice { // 定義一個骰子
let generator: RandomNum // 協議類型的存儲屬性
init(generator: RandomNum) {
self.generator = generator
}
func roll() -> Int { // 產生一個隨機數
return generator.random()
}
}

class RandomNumGenerator1: RandomNum{ // 定義一個類遵守該協議
func random() -> Int {
return Int(arc4random() % 10)
}
}

var d6 = Dice(generator: RandomNumGenerator1()) // 就可以將遵守該協議的類當作參數了
print(d6.roll()) // 隨機數

7、代理設計模式:可以將類或結構體的一些功能委托給其他類型去實現,代理可以用來響應事件或接收外部數據源數據
class Baby {
var needNumFood: Int? // baby需要的食物數量
var babyDelegate: BabyDelegate? // 代理屬性
func eat() { // 吃這個方法
babyDelegate?.feedBaby(baby: self) // 調用代理方法
}
}

class Nanny: BabyDelegate{ // nanny遵守代理
func feedBaby(baby: Baby) { // nanny實現喂食物的代理方法
baby.needNumFood = 10
print("喂baby食物:(baby.needNumFood!)") // 喂baby食物:10
}
}

let baby = Baby()
let nanny = Nanny()

baby.babyDelegate = nanny // 將baby委托給nanny
baby.eat() // baby調用吃的方法委托nanny喂食物

8、在extention中實現協議

protocol SomeProtocol5 {
// 協議內容
}
extension Nanny: SomeProtocol5 { // 在擴展中遵守協議的效果和在原始類中一樣
// 在實際開發中實現協議的時候推薦這樣做,有利于提高代碼的閱讀性
}
9、通過擴展遵守協議:當一個類實現了協議中的方法,卻還沒有遵守該協議時,可以通過空擴展體來遵守該協議

protocol SomeProtocol6 {
var description: String { get }
}
struct Cat { // 并沒有遵守協議
var name: String
var description: String { // 實現協議中的方法
return "A cat named: (name)"
}
}
extension Cat: SomeProtocol6 {} // 在擴展中實現協議

let lucyTheCat = Cat(name: "lucy")
let sp: SomeProtocol6 = lucyTheCat // 遵守協議
print(sp.description) // A cat named: lucy

10、協議本身也是類型,可以放到集合中使用
let things: [SomeProtocol6] = [lucyTheCat] // 用于存放遵守協議的類
for thing in things {
print(thing.description) // A cat named: lucy
}

11、協議的繼承:和類的繼承相似,但協議可以繼承一個或多個其它協議

protocol InheritingProtocol: SomeProtocol5, SomeProtocol6 {
// 任何實現InheritingProtocol協議的同時,也必須實現SomeProtocol5和SomeProtocol6
}

12、 類的專屬協議:通過添加class關鍵字來限制協議只能被類遵守
protocol SomeClassOnlyProtocol: class, InheritingProtocol { // class關鍵字必須出現在最前面
// 如果被結構體或枚舉繼承則會導致編譯錯誤
}

13、協議合成:同時采納多個協議,多個協議之間用&分割,協議的合成并不會生成新的協議類型,只是一個臨時局部的
protocol Name {
var name: String { get }
}
protocol Age {
var age: Int { get }
}

struct People: Name, Age { // 遵守name age這兩個協議
var name: String
var age: Int
}

func say(to people: Name & Age) { // 參數類型: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、檢查協議的一致性,如果不一致可以進行轉換

// is 檢查實例是否符合某個協議,符合返回true,否則返回false
// as? 如果符合某個協議類型,返回類型為協議類型的可選值, 否則返回nil
// as! 將實例強制轉化為某個協議類型,如果失敗會引發運行時錯誤
protocol HasArea { // HasArea協議
var area: Double { get }
}
class Circle: HasArea { // 遵守HasArea協議
let pi = 3.1415927
var radius: Double
var area: Double { return pi * radius * radius}
init(radius: Double) { self.radius = radius }
}

class Country: HasArea { // 遵守HasArea協議
var area: Double
init(area: Double) { self.area = area }
}

class Animal {
var legs: Int
init(legs: Int) { self.legs = legs }
}

15、將所有類對象作為AnyObject對象放到數組中

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協議
print(objectWithArea.area) // 此時的objectWithArea是area協議類型的實例
print(objectWithArea.pi) // ?, 所以只有area屬性才能被訪問
}else {
print("沒有遵守area協議")
}
}

16、協議的可選要求:協議中所有的方法,屬性并不都是一定要實現的,可以在實現和不實現的方法 面前加optional關鍵字,使用可選方法或屬性時,他們的類型會自動變為可選的

// 注意: 可選的協議前面需要加@objc關鍵字.
// @objc:表示該協議暴露給OC代碼,但即使不與OC交互只想實現可選協議要求,還是要加@objc關鍵字.
// 帶有@objc關鍵字的協議只能被OC類,或者帶有@objc關鍵字的類遵守,結構體和枚舉都不能遵守.
@objc protocol CounterDataSource { // 用于計數的數據源
@objc optional var fixAdd: Int { get } // 可選屬性
@objc optional func addForCount(count: Int) -> Int // 可選方法,用于增加數值
}
class Counter: CounterDataSource {
var count = 0 // 用來存儲當前值
var dataSource: CounterDataSource?
func add() { // 增加count值
// 使用可選綁定和兩層可選鏈式調用來調用可選方法
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的數據源設置為ThreeSource
counter.add() // 增加3
counter.add() // 增加3
print(counter.count) // 6

17、協議的擴展:可以通過擴展協議來遵守協議的類型提供屬性方法下標

protocol RandomNumG {
func random() -> Int
}
class RandomNumGen: RandomNumG {
var description: String {
return "RandomNumGen"
}
func random() -> Int {
return Int(arc4random() % 10) // 返回一個0~9的隨機數
}
}

let randomNumG = RandomNumGen()
print(randomNumG.random()) // 0~9的隨機數

extension RandomNumG {
var description: String {
return "extension"
}
func randomBool() -> Bool { // 可以通過擴展來為協議添加方法
return random() > 4 // 隨機數是否大于4
}
}
print(randomNumG.randomBool()) // bool值
print(randomNumG.description) // RandomNumGen,協議擴展中的默認屬性的優先級比自定義屬性低

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容