读书笔记:swift protocol extension

  在objc时代,如果我们想在已有的协议上增加一个方法,并为实现该协议的类增加一个共有的功能,一种常见的做法是将代码拷贝到每一个实现该协议的类中,这是一种笨拙而不便维护的方式。
  swift2.0引入了protocol extension,可以对已有的协议添加拓展,并提供默认实现,在所有遵守协议的实例类型中,即使我们什么也不做,也可以编译通过并调用默认实现。

import Foundation

protocol ExampleProtocol {
    func method()
}
extension ExampleProtocol {
    func method() {
        print("hi")
    }
}

class ExampleClass :ExampleProtocol {
}
ExampleClass().method() //print hi

  在日常开发中,还有一处可以用到extension协议拓展。objc中我们可以通过@optional关键字,声明可选接口类型,表示实例不一定要提供实现,而swift的protocol所有的方法都必须提供实现。
  那么如果我们想在swift中,实现可选协议该怎么办呢?一种可行的方案是在声明protocol的时候加上关键字@objc,其本质上声明一个objc的协议,并且配合@objc optional关键字实现。

import Foundation

@objc protocol ExampleProtocol {
    @objc optional func method()
}
class ExampleClass :ExampleProtocol {
    
} //build succeeded

  这样声明的协议,本质是一个objc的协议,并且只用由class类型实例可以遵守,对swift中struct的enum不可用。
  在swift2.0中可依靠extension提供一种更优雅的方式。extension可以为方法编写默认实现,这样即使我们的实力类型不写任何实现,也可用通过编译。

protocol ExampleProtocol {
    func optionalMethod() //可选
    func necessaryMethod() //必须实现
}
extension ExampleProtocol {
    func optionalMethod() {
        print("hi")
    }
}

class ExampleClass :ExampleProtocol {
    func necessaryMethod() {
        print("necessaryMethod")
    }
    //由于extension为optionalMethod提供了默认实现
    //所以我们这里可以不再重写对应的实现
}

  同样,如果在protocol中没有显示声明的方法口,而只在extension中提供了默认实现,也是可以作为可选接口,并通过编译。

protocol ExampleProtocol {
    
}
extension ExampleProtocol {
    func optionalMethod() {
        print("hi")
    }
}

class ExampleClass :ExampleProtocol {
    
}
//build succeeded

  这种在protocol中没有显示声明的方法,而直接在extension中提供了默认实现,容易产生一种迷惑。比如我们定义了这样的一个接口和它的一个扩展:

protocol ExampleProtocol {
    //协议中显示声明的方法
    func explicitMethod()
}

extension ExampleProtocol {
    //协议中显示声明的方法
    func explicitMethod() {
        print("hi")
    }
    //协议中未显示声明的方法
    func extensionMethod() {
        print("hi")
    }
}

class ExampleClass :ExampleProtocol {
    func explicitMethod() {
        print("hello")
    }
    func extensionMethod() {
        print("hello")
    }
}

  在调用的时候,没有疑问,2个方法输出的都是hello:

let cls = ExampleClass()
cls.explicitMethod()  //hello
cls.extensionMethod() //hello

  而如果我们将cls的类型,强转为协议类型,考虑此时的输出是什么?

let cls = ExampleClass() as ExampleProtocol
cls.explicitMethod()  //hello
cls.extensionMethod() //hi

  此时的输出似乎出乎了我们的意料,我们都知道cls的真实类型是ExampleClass,并且我们重写了协议的实现,而运行时却输出了extension中的默认实现。
  产生这种问题的原因是,由于我们没有显示声明extensionMethod,这个方法实际上变成了可选类型,没有任何规定遵守该协议的类型必须实现,而我们的编译器认为extensionMethod有能未被当前类型的实例实现,转而不使用动态派发的机制,而是在编译期就确定调用哪个具体的实现,以保证安全。
  而对于方法explicitMethod,由于在protocol中显示的声明了,因此可以确定遵守该协议的实例一定是实现了该方法(这里不管是重写实现,还是extension的默认实现),可以大胆的使用动态派发机制,寻找方法的最终实现。

总结一下:

  • extension允许我们为已有协议增加方法或者增加方法的默认实现。
  • swift protocol中显示声明的方法,必须实现,但extension提供了默认实现的方法,实例类型可以不提供显示的具体实现(隐示实现了extension的实现)。
  • extension的默认实现可以让方法变为可选,即实例不需要提供最终实现。
  • 如果方法没有在protocol中显示的声明,类型推断得到是实例类型将会调用实例的最终实现(如果有,没有则调用extension中的默认实现),而如果类型推断的是协议类型,将会调用extension的默认实现。

本文为读书笔记,参考连接
PROTOCOL EXTENSION
可选接口和接口扩展

你可能感兴趣的:(读书笔记:swift protocol extension)