国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Swift > 正文

Swift對比Kotlin之接口淺析

2024-07-21 23:03:36
字體:
供稿:網(wǎng)友

Kotlin 語言和 Swift 語言在一個(gè)團(tuán)隊(duì)一起開發(fā)已經(jīng)很久了,由于平臺的差異性,他們經(jīng)常會進(jìn)行一些技術(shù)上的交流(PK),「Kotlin vs. Swift」 系列就是他們在互相切磋時(shí)的語錄。內(nèi)容會由簡及深,慢慢深入。

技術(shù)漫談

Swift:

Hi, Kotlin 君, Swift 4 發(fā)布了,我們今天就基于 Swift 4 的新語法來討論一下接口吧?

Kotlin:

好啊,接口對我們開發(fā)來說是個(gè)很重要的概念。設(shè)計(jì)模式中要求我們寫代碼要遵循依賴倒置原則,就是程序要依賴于抽象接口,不要依賴于具體實(shí)現(xiàn),也就是要求我們要面向接口編程。

Swift:

是的,在 Swift 中,接口被稱為協(xié)議(即 Protocol ), 蘋果大大強(qiáng)化了 Protocol 在這門語言中的地位,整個(gè) Swift 標(biāo)準(zhǔn)庫也是基于 Protocol 來設(shè)計(jì)的,可以說 Swift 是一門面向 protocol 編程的語言。

Kotlin:

聽起來好流比,那來說說你們是怎么定義接口的?

Swift:

我們用 Protocol 關(guān)鍵字來定義接口:

protocol SomeProtocol {

func f()

}

你們呢?

Kotlin:

我們同 Java 一樣,用 interface 關(guān)鍵字來定義接口:

interface MyInterface {

fun f()

}

Swift:

嗯,看起來就是關(guān)鍵字不一樣。你們怎么實(shí)現(xiàn)接口呢?

Kotlin:

一個(gè)類要實(shí)現(xiàn)某個(gè)接口,需要在類型名稱后加上協(xié)議名稱,中間以冒號(:)分隔:

class MyClass: MyInterface {

override fun f() {

// 具體實(shí)現(xiàn)

}

}

一個(gè)類或者對象可以實(shí)現(xiàn)一個(gè)或多個(gè)接口。實(shí)現(xiàn)多個(gè)接口時(shí),各接口之間用逗號(,)分隔.

Swift:

我們也是一樣的,只是我們不需要寫 override 關(guān)鍵字,只有當(dāng)子類復(fù)寫父類的方法或計(jì)算屬性時(shí)才需要用 override 修飾。另外,我們還可以通過擴(kuò)展類型來實(shí)現(xiàn)協(xié)議:

class MyClass {

//...類的定義

}

extension MyClass: SomeProtocol {

func f() {

// 具體實(shí)現(xiàn)

}

}

Kotlin:

這意味著你們不用修改原有類型,就可以讓原有類型符合某個(gè)協(xié)議了,甚至可以擴(kuò)展標(biāo)準(zhǔn)庫中的某個(gè)基礎(chǔ)類型來實(shí)現(xiàn)自定義的協(xié)議。這很符合開閉原則嘛。

Swift:

是啊,牛不牛 。

我們實(shí)現(xiàn)協(xié)議的類型除了 class 外,還可以是 struct 或 enum。

Kotlin:

Kotlin 沒有結(jié)構(gòu)體的概念, enum 也可以實(shí)現(xiàn)接口。

來說說你們的接口中可以聲明哪些東西吧?

Swift:

我們可以在協(xié)議中聲明屬性和方法,用 var 關(guān)鍵字來聲明變量屬性,并在屬性聲明后加上 { set get } 來表示屬性是可讀可寫的,用 { get } 來表示屬性是只讀的。

協(xié)議里面聲明的屬性和方法一定是抽象的,不能有實(shí)現(xiàn),由符合協(xié)議的類型來提供所有屬性和方法的實(shí)現(xiàn)。

Kotlin:

我們也可以聲明屬性和方法,而且 Kotlin 可以直接在接口中為屬性和方法提供默認(rèn)實(shí)現(xiàn):

interface MyInterface {

val prop: Int // 抽象的

val propertyWithImplementation: String

get() = "foo"

fun foo() {

print(prop)

}

}

class MyClass : MyInterface {

override val prop: Int = 29

}

Swift:

雖然我們不能在協(xié)議中直接提供屬性和方法的默認(rèn)實(shí)現(xiàn),但是我們可以通過協(xié)議擴(kuò)展來達(dá)到此目的。

protocol MyProtocol {

var prop: Int { get set }

var propertyWithImplementation: String { get }

func foo()

}

extension MyProtocol {

var propertyWithImplementation: String {

return "foo"

}

func foo() {

print(prop)

}

}

class MyClass: MyProtocol {

var prop: Int = 29

}

Kotlin:

哇~,你們這個(gè)協(xié)議擴(kuò)展有點(diǎn)厲害了。

Swift:

是的,正是這個(gè)特性,才使得我們面向協(xié)議編程成為可能。我們甚至可以在擴(kuò)展中添加協(xié)議里沒有定義過的方法和屬性。

extension MyProtocol {

func isExceed() -> Bool {

return prop > 30

}

}

let instance = MyClass()

print(instance.isExceed())

// false

Kotlin:

這就意味著你們也有能力擴(kuò)展標(biāo)準(zhǔn)庫里的協(xié)議了,可以很方便的給標(biāo)準(zhǔn)庫里的協(xié)議添加新的方法和屬性。

Swift:

聰明,確實(shí)是這樣。不僅如此,在擴(kuò)展協(xié)議的時(shí)候,還可以指定一些限制條件,只有遵循協(xié)議的類型滿足這些限制條件時(shí),才能獲得協(xié)議擴(kuò)展提供的默認(rèn)實(shí)現(xiàn)。

protocol TextRepresentable {

var textualDeion: String { get }

}

struct Hamster: TextRepresentable {

var name: String

var textualDeion: String {

return "A hamster named (name)"

}

}

extension Collection where Iterator.Element: TextRepresentable {

var textualDeion: String {

let itemsAsText = self.map { $0.textualDeion }

return "[" + itemsAsText.joined(separator: ", ") + "]"

}

}

let hamsters = [Hamster(name: "Jim"), Hamster(name: "Merry")]

print(hamsters.textualDeion)

// [A hamster named Jim, A hamster named Merry]

這里擴(kuò)展了 Swift 標(biāo)準(zhǔn)庫中的 Collection 協(xié)議,但是限制只適用于集合中的元素遵循了 TextRepresentable 協(xié)議的情況。 因?yàn)?Swift 中 Array 符合 Collection 協(xié)議,而 Hamster 類型又符合 TextRepresentable 協(xié)議,所以 hamsters 可以使用 textualDeion 屬性得到數(shù)組內(nèi)容的文本表示。

Kotlin:

贊啊~,你們這個(gè)協(xié)議擴(kuò)展太強(qiáng)大了,不僅可以擴(kuò)展自己定義的協(xié)議,還可以擴(kuò)展標(biāo)準(zhǔn)庫中的協(xié)議,怪不得蘋果稱 Swift 是面向協(xié)議編程的語言。

Swift 在實(shí)現(xiàn)多個(gè)協(xié)議時(shí),會不會有不同協(xié)議帶來同名方法或?qū)傩缘臎_突的問題?

Swift:

我們還不能很好地處理多個(gè)協(xié)議的沖突問題。

Kotlin:

Kotlin 可以,Kotlin 有一套規(guī)則來處理這樣的沖突。在 Kotlin 中,如果一個(gè)類從它的直接超類繼承相同成員的多個(gè)實(shí)現(xiàn)(由于接口函數(shù)可以有實(shí)現(xiàn)),它必須覆蓋這個(gè)成員并提供其自己的實(shí)現(xiàn)。 為了表示采用從哪個(gè)超類型繼承的實(shí)現(xiàn),我們使用由尖括號中超類型名限定的 super,如 super。

open class A {

open fun f() { print("A") }

fun a() { print("a") }

}

interface B {

fun f() { print("B") } // interface members are 'open' by default

fun b() { print("b") }

}

class C() : A(), B {

// The compiler requires f() to be overridden:

override fun f() {

super.f() // call to A.f()

.f() // call to B.f()

super

}

}

Swift:

這個(gè)好贊,可以不怕名字沖突了 。

Kotlin 的接口中可以聲明類方法嗎?

Kotlin:

Kotlin 里面已經(jīng)沒有類方法的概念了。

Swift:

我們可以在協(xié)議中使用 static 關(guān)鍵字來聲明類型方法,如果實(shí)現(xiàn)該協(xié)議的類型是 class 類型,則在實(shí)現(xiàn)類中除了用 static 來修飾類型方法外,也可以使用 class關(guān)鍵字.

protocol SomeProtocol {

static func someTypeMethod()

}

class SomeClass: SomeProtocol {

// 這里也可以用 static 修飾,區(qū)別是 static 修飾的屬性

// 或方法不能被子類復(fù)寫,class 修飾的可以被子類復(fù)寫

class func someTypeMethod() {

print("type method")

}

}

Kotlin:

我們的接口雖然不支持類方法,但是我們可以給接口中定義的方法的參數(shù)設(shè)置默認(rèn)值。

Swift:

這。。。我們不支持為協(xié)議中的方法的參數(shù)提供默認(rèn)值。

Kotlin:

方法參數(shù)的默認(rèn)值必須定義在接口中,在實(shí)現(xiàn)類或?qū)ο髮?shí)現(xiàn)該方法時(shí),不能為函數(shù)提供默認(rèn)值。同時(shí)接口的中函數(shù)不能用 JVMOverride 注解修飾,所以接口中定義的帶有默認(rèn)值的參數(shù),不能為 Java 生成重載方法,如果接口是定義在庫里面,Kotlin 的實(shí)現(xiàn)也無法使用自動(dòng)重載功能,需要手動(dòng)重載。

interface IDownload{

fun(url: String, isSupportBreakpointC: Boolean = true)

}

class DownloadImpl: IDownload{

override fun(url: String, isSupportBreakpointC: Boolean){

}

}

Swift:

這點(diǎn)算你強(qiáng)。

我們的協(xié)議中可以定義可變方法,如果協(xié)議中定義的實(shí)例方法會改變遵循該協(xié)議的類型的實(shí)例,那么需要在該方法前加 mutating 關(guān)鍵字, 表示可以在該方法中修改它所屬的實(shí)例以及實(shí)例的任意屬性的值, 例如:

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()

// lightSwitch 現(xiàn)在的值為 .On

Kotlin:

我們沒這特性,這點(diǎn)你贏了。

Swift:

豈止如此,我們的協(xié)議中還可以要求遵循協(xié)議的類型實(shí)現(xiàn)指定的構(gòu)造器:

protocol SomeProtocol {

init(someParameter: Int)

}

class SomeClass: SomeProtocol {

required init(someParameter: Int) {

// initializer implementation goes here

}

}

在符合協(xié)議的類中實(shí)現(xiàn)構(gòu)造器,必須在構(gòu)造器實(shí)現(xiàn)前加上 required 修飾符。使用 required 修飾符可以確保所有子類也必須提供此構(gòu)造器實(shí)現(xiàn),從而也能符合協(xié)議。 如果類已經(jīng)被標(biāo)記為 final,那么不需要在協(xié)議構(gòu)造器的實(shí)現(xiàn)中使用 required 修飾符,因?yàn)?final 類不能有子類.

協(xié)議還可以為遵循協(xié)議的類型定義可失敗構(gòu)造器。

Kotlin:

好吧,我們不可以在接口中聲明構(gòu)造器。

Swift:

你們的接口可以繼承嗎?

Swift 中協(xié)議能夠繼承一個(gè)或多個(gè)其他協(xié)議,可以在繼承的協(xié)議的基礎(chǔ)上增加新的要求.

Kotlin:

當(dāng)然可以,這是基本的用法好伐。

Swift:

好吧。。我們還可以通過讓協(xié)議繼承 AnyObject 協(xié)議來限制協(xié)議只能被 Class 類型遵循,而結(jié)構(gòu)體或枚舉不能遵循該協(xié)議。

Kotlin:

我們并沒有這種限制,接口可以被類和枚舉實(shí)現(xiàn)。

Swift:

你們的接口可以組合嗎?

Swift 可以采用 & 符號將多個(gè)協(xié)議進(jìn)行組合:

protocol Named {

var name: String { get }

}

protocol Aged {

var age: Int { get }

}

struct Person: Named, Aged {

var name: String

var age: Int

}

func wishHappyBirthday(to celebrator: Named & Aged) {

print("Happy birthday, (celebrator.name), you're (celebrator.age)!")

}

let birthdayPerson = Person(name: "Malcolm", age: 21)

wishHappyBirthday(to: birthdayPerson)

// Prints "Happy birthday, Malcolm, you're 21!"

這里 wishHappyBirthday(to:) 函數(shù)的參數(shù)類型為 Named & Aged, 這意味著它不關(guān)心參數(shù)的具體類型,只要參數(shù)符合這兩個(gè)協(xié)議即可。當(dāng)然也可以給組合的協(xié)議指定一個(gè)別名:typealias Property = Named & Aged

Kotlin:

666,你們的協(xié)議真是玩出花了,這個(gè)功能我們也沒有??。

Swift:

除了協(xié)議與協(xié)議組合外,協(xié)議還可以與類進(jìn)行組合:

class Location {

var latitude: Double

var longitude: Double

init(latitude: Double, longitude: Double) {

self.latitude = latitude

self.longitude = longitude

}

}

class City: Location, Named {

var name: String

init(name: String, latitude: Double, longitude: Double) {

self.name = name

super.init(latitude: latitude, longitude: longitude)

}

}

func beginConcert(in location: Location & Named) {

print("Hello, (location.name)!")

}

let seattle = City(name: "Seattle", latitude: 47.6, longitude: -122.3)

beginConcert(in: seattle)

這里的 beginConcert(in:) 函數(shù)的參數(shù)要求是 Location 的子類,且必須符合 Named 協(xié)議.

Kotlin:

太贊了~,給你點(diǎn)32個(gè)贊!

Swift:

你們是怎么判斷某個(gè)實(shí)例是否符合某個(gè)協(xié)議的?

Kotlin:

這就是判斷某個(gè)對象是否是某個(gè)類型嘛,當(dāng)然是用 is 啦,如果是類型轉(zhuǎn)換的話用 as 。

Swift:

嗯嗯,這點(diǎn)我們是一致的。

你們可以定義接口中的方法或?qū)傩詾榭蛇x嗎?

Kotlin:

何謂可選?

Swift:

就是可以實(shí)現(xiàn)也可以不實(shí)現(xiàn)

Kotlin:

前面講過了啊,如果接口中的屬性或方法在實(shí)現(xiàn)類中可以實(shí)現(xiàn)也可以不實(shí)現(xiàn),則可以在接口定義中為該方法提供默認(rèn)實(shí)現(xiàn)。

Swift:

嗯,Swift 是通過協(xié)議擴(kuò)展提供默認(rèn)實(shí)現(xiàn)來到達(dá)可選的目的。

不過 Swift 也可以像 Objective-C 里那樣定義可選的接口方法,就需要在 protocol 定義之前加上 @objc,將 protocol 變?yōu)?Objective-C 的。然后使用 optional 關(guān)鍵字來聲明某些方法或?qū)傩栽诜显搮f(xié)議的類中可以不實(shí)現(xiàn),如下:

@objc protocol CounterDataSource {

@objc optional func incrementForCount(count: Int) -> Int

@objc optional var fixedIncrement: Int { get }

}

需要注意的是,標(biāo)記為 @objc 的 protocol 只能被 class 實(shí)現(xiàn),不能被 struct 和 enum 類型實(shí)現(xiàn),而且實(shí)現(xiàn)它的 class 中的方法也必須被標(biāo)注為 @objc,或者整個(gè)類就是繼承自 NSObject。

Kotlin:

額。。。這豈不是很蛋疼

Swift:

是的,所以這種方式并不提倡。

Swift 可以使用 Protocol 來實(shí)現(xiàn)委托(代理)模式,委托(代理)模式允許類或結(jié)構(gòu)體將一些需要它們負(fù)責(zé)的功能委托給其他類型的實(shí)例,如下:

protocol RentHouserDelegate{

func rent(_ name:String);

}

class Tenant {

var name = "lucy"

var delegate: RentHouserDelegate?

func rentHouse(){

delegate?.rent(name)

}

}

class Intermediary: RentHouserDelegate {

var name="lily"

func rent(_ name:String) {

print("(name) 請 (self.name) 幫她租一套房子");

}

}

var person = Tenant();

person.delegate = Intermediary()

person.rentHouse()

// lucy 請 lily 幫她租一套房子

Kotlin:

這是接口的一種常用方法,我們依賴注入框架就大量使用這種方式。

Swift:

哈哈,英雄所見略同。

好了,就到這吧,今天的PK互有攻防,好帶勁??~

Kotlin:

總體來說還是你們的協(xié)議比較強(qiáng)大。

Swift:

那是,要不然蘋果怎么敢稱 Swift 是一門面向協(xié)議編程的語言呢

Kotlin:

好吧,咱們來日方長。

Swift:

嗯嗯,后會有期。

知識點(diǎn)總結(jié)

Kotlin

接口定義

同 Java 一樣,Kotlin 用 interface 關(guān)鍵字來定義接口,Kotlin 接口中可以有函數(shù)的實(shí)現(xiàn),也可以只有抽象方法,接口無法保存狀態(tài),它可以有屬性但必須聲明為抽象或提供訪問器實(shí)現(xiàn)。

interface MyInterface {

fun bar()

fun foo() {

// 可選的方法體

}

}

實(shí)現(xiàn)接口

Kotlin 的一個(gè)類或者對象可以實(shí)現(xiàn)一個(gè)或多個(gè)接口。由于 Kotlin 接口本身的函數(shù)式可以有實(shí)現(xiàn)的,所以在一個(gè)類或?qū)ο髮?shí)現(xiàn)多個(gè)接口的時(shí)候,就有可能發(fā)生沖突,這包括接口之間的的成員沖突,也包括接口與父類直接的成員沖突。

覆蓋沖突

在 Kotlin 中,如果一個(gè)類從它的直接超類繼承相同成員的多個(gè)實(shí)現(xiàn)(由于接口函數(shù)可以有實(shí)現(xiàn)),它必須覆蓋這個(gè)成員并提供其自己的實(shí)現(xiàn)。 為了表示采用從哪個(gè)超類型繼承的實(shí)現(xiàn),我們使用由尖括號中超類型名限定的 super,如 super

open class A {

open fun f() { print("A") }

fun a() { print("a") }

}

interface B {

fun f() { print("B") } // interface members are 'open' by default

fun b() { print("b") }

}

class C() : A(), B {

// The compiler requires f() to be overridden:

override fun f() {

super.f() // call to A.f()

 

super.f() // call to B.f()

}

}

同時(shí)繼承 A 和 B 沒問題,并且 a() 和 b() 也沒問題因?yàn)?C 只繼承了每個(gè)函數(shù)的一個(gè)實(shí)現(xiàn)。 但是 f() 由 C 繼承了兩個(gè)實(shí)現(xiàn),所以我們必須在 C 中覆蓋 f() 并且提供我們自己的實(shí)現(xiàn)來消除歧義。

接口中的屬性

Kotlin 中可以在接口中定義屬性。在接口中聲明的屬性要么是抽象的,要么提供訪問器的實(shí)現(xiàn)。在接口中聲明的屬性不能有幕后字段(backing field),因此接口中聲明的訪問器不能引用它們。

interface MyInterface {

val prop: Int // 抽象的

val propertyWithImplementation: String

get() = "foo"

fun foo() {

print(prop)

}

}

class Child : MyInterface {

override val prop: Int = 29

}

函數(shù)默認(rèn)參數(shù)與重載

如果接口函數(shù)需要定義默認(rèn)值的話,必須定義在接口中,在實(shí)現(xiàn)類或?qū)ο髮?shí)現(xiàn)該方法時(shí),不能為函數(shù)提供默認(rèn)值。同時(shí)接口的中函數(shù)不能用 JVMOverride 注解修飾,所以接口中定義的帶有默認(rèn)值的參數(shù),不能為 Java 生成重載方法,如果接口是定義在庫里面,Kotlin 的實(shí)現(xiàn)也無法使用自動(dòng)重載功能,需要手動(dòng)重載。

interface IDownload{

fun(url: String, isSupportBreakpointC: Boolean = true)

}

class DownloadImpl{

override fun(url: String, isSupportBreakpointC: Boolean){

}

}

Swift

Protocol

Swift 是一門支持面向協(xié)議編程的語言,在 Swift 語言中,協(xié)議被賦予了更多的功能和更廣闊的使用空間。恰逢蘋果發(fā)布了 swift 4,以下都是基于最新的 swift 4 語法進(jìn)行講述。

協(xié)議語法

協(xié)議聲明:

protocol SomeProtocol {

// protocol definition goes here

}

要讓自定義類型符合某個(gè)協(xié)議,需要在類型名稱后加上協(xié)議名稱,中間以冒號(:)分隔。符合多個(gè)協(xié)議時(shí),各協(xié)議之間用逗號(,)分隔. swift 中,符合協(xié)議的類型可以是 class、struct 或 enum。

struct SomeStructure: FirstProtocol, AnotherProtocol {

// structure definition goes here

}

需要注意的是,如果某個(gè)類在符合某個(gè)協(xié)議的同時(shí)又繼承自某個(gè)父類,應(yīng)將其父類名放在其符合的協(xié)議名之前。

協(xié)議屬性聲明

協(xié)議中可以聲明符合此協(xié)議的類型必須實(shí)現(xiàn)的屬性:

protocol SomeProtocol {

var mustBeSettable: Int { get set }

var doesNotNeedToBeSettable: Int { get }

}

協(xié)議不指定屬性是存儲型屬性還是計(jì)算型屬性,它只指定屬性的名稱和類型,以及屬性是可讀的還是可讀可寫的。

協(xié)議總是用 var 關(guān)鍵字來聲明變量屬性,在類型聲明后加上 { set get } 來表示屬性是可讀可寫的,用 { get } 來表示屬性是只讀的。

協(xié)議中總是使用 static 關(guān)鍵字定義類型屬性,如果是 class類型實(shí)現(xiàn)協(xié)議,除了 static,還可以使用 class 關(guān)鍵字來聲明類型屬性。

protocol AnotherProtocol {

static var someTypeProperty: Int { get }

}

class SomeClass: AnotherProtocol {

// 這里也可以用 static 修飾,區(qū)別是 static 修飾的屬性

或方法不能被子類復(fù)寫,class 修飾的可以被子類復(fù)寫

class var someTypeProperty: Int {

return 0

}

}

協(xié)議方法聲明

協(xié)議可以要求遵循協(xié)議的類型實(shí)現(xiàn)某些指定的實(shí)例方法或類方法。需要注意的是,不支持為協(xié)議中的方法的參數(shù)提供默認(rèn)值。

protocol RandomNumberGenerator {

func random() -> Double

}

與屬性類似,在協(xié)議中也使用 static 定義類方法,當(dāng) class 類型實(shí)現(xiàn)協(xié)議時(shí),可以使用 class 關(guān)鍵字來修飾.

如果協(xié)議中定義的實(shí)例方法會改變遵循該協(xié)議的類型的實(shí)例,那么需要在該方法前加 mutating 關(guān)鍵字, 表示可以在該方法中修改它所屬的實(shí)例以及實(shí)例的任意屬性的值, 例如:

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()

// lightSwitch 現(xiàn)在的值為 .On

協(xié)議構(gòu)造器聲明

協(xié)議可以要求遵循協(xié)議的類型實(shí)現(xiàn)指定的構(gòu)造器:

protocol SomeProtocol {

init(someParameter: Int)

}

class SomeClass: SomeProtocol {

required init(someParameter: Int) {

// initializer implementation goes here

}

}

在符合協(xié)議的類中實(shí)現(xiàn)構(gòu)造器,必須在構(gòu)造器實(shí)現(xiàn)前加上 required 修飾符。使用 required 修飾符可以確保所有子類也必須提供此構(gòu)造器實(shí)現(xiàn),從而也能符合協(xié)議. 如果類已經(jīng)被標(biāo)記為 final,那么不需要在協(xié)議構(gòu)造器的實(shí)現(xiàn)中使用 required 修飾符,因?yàn)?final 類不能有子類.

協(xié)議還可以為遵循協(xié)議的類型定義可失敗構(gòu)造器。

委托(代理)模式

Swift 可以使用 Protocol 來實(shí)現(xiàn)委托(代理)模式,委托(代理)模式允許類或結(jié)構(gòu)體將一些需要它們負(fù)責(zé)的功能委托給其他類型的實(shí)例,如下:

protocol RentHouserDelegate{

func rent(_ name:String);

}

class Tenant {

var name = "lucy"

var delegate: RentHouserDelegate?

func rentHouse(){

delegate?.rent(name)

}

}

class Intermediary: RentHouserDelegate {

var name="lily"

func rent(_ name:String) {

print("(name) 請 (self.name) 幫她租一套房子");

}

}

var person = Tenant();

person.delegate = Intermediary()

person.rentHouse()

// lucy 請 lily 幫她租一套房子

通過擴(kuò)展遵循協(xié)議

可以通過擴(kuò)展令已有類型遵循并符合協(xié)議:

protocol TextRepresentable {

var textualDeion: String { get }

}

struct Circular {

var radius: Int

}

extension Circular: TextRepresentable {

var textualDeion: String {

return "The circular's radius is (radius)"

}

}

let circular = Circular(radius: 2)

print(circular.textualDeion)

// The circular's radius is 2

當(dāng)一個(gè)類型已經(jīng)符合了某個(gè)協(xié)議中的所有要求,卻還沒有聲明遵循該協(xié)議時(shí),可以通過空擴(kuò)展來使該類型遵循該協(xié)議:

struct Square {

var width: Int

var textualDeion: String {

return "The square's width is (width)"

}

}

extension Square: TextRepresentable {}

let square = Square(width: 3)

let squareTextRepresentable: TextRepresentable = square

print(squareTextRepresentable.textualDeion)

// The square's width is 3

協(xié)議類型

盡管協(xié)議本身并未實(shí)現(xiàn)任何功能,但是協(xié)議可以被當(dāng)做一個(gè)成熟的類型來使用。協(xié)議類型也可以在數(shù)組或者字典這樣的集合中使用:

let things: [TextRepresentable] = [circular, square]

for thing in things {

print(thing.textualDeion)

}

協(xié)議的繼承

協(xié)議能夠繼承一個(gè)或多個(gè)其他協(xié)議,可以在繼承的協(xié)議的基礎(chǔ)上增加新的要求:

protocol PrettyTextRepresentable: TextRepresentable {

var prettyTextualDeion: String { get }

}

extension Square: PrettyTextRepresentable {

var prettyTextualDeion: String {

var output = textualDeion + ": "

output += "the area is (width*width)"

return output

}

}

print(square.prettyTextualDeion)

// The square's width is 3: the area is 9

Class 類型專屬協(xié)議

通過讓協(xié)議繼承 AnyObject 協(xié)議來限制協(xié)議只能被 Class 類型遵循,而結(jié)構(gòu)體或枚舉不能遵循該協(xié)議

protocol SomeClassOnlyProtocol: AnyObject, SomeInheritedProtocol {

// class-only protocol definition goes here

}

協(xié)議合成

可以采用 & 符號將多個(gè)協(xié)議進(jìn)行組合:

protocol Named {

var name: String { get }

}

protocol Aged {

var age: Int { get }

}

struct Person: Named, Aged {

var name: String

var age: Int

}

func wishHappyBirthday(to celebrator: Named & Aged) {

print("Happy birthday, (celebrator.name), you're (celebrator.age)!")

}

let birthdayPerson = Person(name: "Malcolm", age: 21)

wishHappyBirthday(to: birthdayPerson)

// Prints "Happy birthday, Malcolm, you're 21!"

這里 wishHappyBirthday(to:) 函數(shù)的參數(shù)類型為 Named & Aged, 這意味著它不關(guān)心參數(shù)的具體類型,只要參數(shù)符合這兩個(gè)協(xié)議即可。當(dāng)然也可以給組合的協(xié)議指定一個(gè)別名:typealias Property = Named & Aged

除了協(xié)議與協(xié)議組合外,協(xié)議還可以與 class 進(jìn)行組合:

class Location {

var latitude: Double

var longitude: Double

init(latitude: Double, longitude: Double) {

self.latitude = latitude

self.longitude = longitude

}

}

class City: Location, Named {

var name: String

init(name: String, latitude: Double, longitude: Double) {

self.name = name

super.init(latitude: latitude, longitude: longitude)

}

}

func beginConcert(in location: Location & Named) {

print("Hello, (location.name)!")

}

let seattle = City(name: "Seattle", latitude: 47.6, longitude: -122.3)

beginConcert(in: seattle)

這里的 beginConcert(in:) 函數(shù)的參數(shù)要求是 Location 的子類,且必須符合 Named 協(xié)議.

檢查協(xié)議一致性

可以通過 is as? as 來檢查某個(gè)實(shí)例是否符合某個(gè)協(xié)議:

let things: [Any] = [circular, square, "abc"]

for thing in things {

if let object = thing as? TextRepresentable {

print(object.textualDeion)

} else {

print("It does not conform to TextRepresentable")

}

}

可選協(xié)議

原生的 Swift protocol 里沒有可選項(xiàng),所有定義的方法都是必須實(shí)現(xiàn)的。如果想要像 Objective-C 里那樣定義可選的接口方法,就需要在 protocol 定義之前加上 @objc,將 protocol 變?yōu)?Objective-C 的。然后使用 optional 關(guān)鍵字來聲明某些方法或?qū)傩栽诜显搮f(xié)議的類中可以不實(shí)現(xiàn),如下:

@objc protocol CounterDataSource {

@objc optional func incrementForCount(count: Int) -> Int

@objc optional var fixedIncrement: Int { get }

}

需要注意的是,標(biāo)記為 @objc 的 protocol 只能被 class 實(shí)現(xiàn),不能被 struct 和 enum 類型實(shí)現(xiàn),而且實(shí)現(xiàn)它的 class 中的方法還必須也被標(biāo)注為 @objc,或者整個(gè)類就是繼承自 NSObject。這還是很蛋疼的。

協(xié)議擴(kuò)展

協(xié)議可以通過擴(kuò)展來為遵循協(xié)議的類型提供屬性和方法的實(shí)現(xiàn),即使協(xié)議中沒有聲明。這樣就無需在每個(gè)遵循協(xié)議的類型中都重復(fù)同樣的實(shí)現(xiàn),也無需使用全局函數(shù)。

protocol TextRepresentable {

var textualDeion: String { get }

}

extension TextRepresentable {

func hasDeion() -> Bool {

return !textualDeion.isEmpty

}

}

還可以通過協(xié)議擴(kuò)展來為協(xié)議要求的屬性、方法提供默認(rèn)的實(shí)現(xiàn)。這樣在遵循這個(gè)協(xié)議的類型中,可以不用實(shí)現(xiàn)這個(gè)屬性或方法,調(diào)用的時(shí)候默認(rèn)調(diào) extension 中的實(shí)現(xiàn)。這也相當(dāng)于變相將 protocol 中的屬性或方法設(shè)定為了 `optional.

extension TextRepresentable {

var textualDeion: String {

return "This is a shape"

}

}

在擴(kuò)展協(xié)議的時(shí)候,也可以指定一些限制條件,只有遵循協(xié)議的類型滿足這些限制條件時(shí),才能獲得協(xié)議擴(kuò)展提供的默認(rèn)實(shí)現(xiàn)。

extension Collection where Iterator.Element: TextRepresentable {

var textualDeion: String {

let itemsAsText = self.map { $0.textualDeion }

return "[" + itemsAsText.joined(separator: ", ") + "]"

}

}

let circulars = [Circular(radius: 1), Circular(radius: 2)]

print(circulars.textualDeion)

// [The circular's radius is 1, The circular's radius is 2]

這里擴(kuò)展了 Collection 協(xié)議,但是限制只適用于集合中的元素遵循了 TextRepresentable 協(xié)議的情況。 因?yàn)?Swift 中 Array 符合 Collection 協(xié)議,而 Circular 類型又符合 TextRepresentable 協(xié)議,所以 circulars 可以使用 textualDeion 屬性得到數(shù)組內(nèi)容的文本表示

文章轉(zhuǎn)載自網(wǎng)絡(luò)


注:相關(guān)教程知識閱讀請移步到kotlin教程頻道。
發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 荔波县| 屯留县| 北安市| 旌德县| 金堂县| 涿州市| 通化市| 法库县| 喀喇沁旗| 伊吾县| 西昌市| 锦州市| 正蓝旗| 新沂市| 锦州市| 吉水县| 微山县| 惠水县| 陆丰市| 舟山市| 北碚区| 怀化市| 兴义市| 桃园县| 乌鲁木齐市| 福安市| 双柏县| 南岸区| 鸡泽县| 龙南县| 丰台区| 安福县| 禹州市| 九龙坡区| 六盘水市| 昭通市| 云龙县| 时尚| 通榆县| 古丈县| 昌都县|