先从 Swift 协议扩展的语法说起
注:协议扩展 Protocol extension: Swift 1.x
中,extension
仅 只能作⽤在实际的类型上 (也就是 class
, struct
等等),⽽不能扩展⼀个 protocol
.
我们先来看一个例子,从例子中声明协议扩展的语法,然后引出为什么要使用协议扩展,它和 OC
之间的对比在哪里
协议扩展语法的代码
protocol P {
func method1() -> String
}
extension P {
func method1() -> String {
return "hi"
}
func method2() -> String {
return "hi"
}
}
struct S: P {
func method1() -> String {
return "hello"
}
func method2() -> String {
return "hello"
}
}
let s = S()
let p = s as P
print(p.method1()) // hello
print(p.method2()) // hi
print("---")
print(s.method1()) // hello
print(s.method2()) // hello
说明
上面这段代码,节选自喵神的 《Swifter tips》. 我稍微将协议的名称和结构体的名称作了修改,便于自己阅读。
-
P
代表的是protocol
-
S
代表的是struct
分析
-
三处声明:
-
protocol P
定义了一个协议,协议中有一个method1
的方法。这些都和oc
的协议定义相同,没什么特别的。 -
extension P
字面意思是对协议P
的扩展,实际上表示的是我给 协议P
添加一些函数的默认实现,这些实现,可以是协议中已有的method1
,也可以是我追加的方法method2
.当类或结构体遵守这个协议的时候,不需要额外声明,就可以调用协议扩展中声明并实现的方法。 -
struct S: P
的声明如字面意思,声明了一个遵守协议P
的结构体。他去重载了协议扩展中的两个方法。因此在正常调用的时候,会调用重载后的方法,也就是打印出hello
。
struct S: P
中实际上是可以不去写method1
和method2
的实现的。因为协议扩展中已经给出了它们的基本实现
-
-
s
和p
调用结果为什么不同?
如代码中声明,这里的s
是S
这个结构体的实例,p
实际上是某个遵循P
协议的结构体/类的实例。
因此,在实际调用时,s
很明确的表明了,自己是S
这个结构体下面的,而p
我不知道我到底是谁家的,那么我只能用协议默认的了。
s
的这种具体实现的情况,叫做swift
中的动态派发(我应该没有说错)关于动态派发,有一篇 参考文章
协议扩展解决了什么问题
- Swift 协议中怎么实现
OC
协议中的@option
功能 - 实现类似于
OC
中category
的效果,在swift
上称为「命名空间形式扩展」
关于第一点,
《Swifter-tips》 中 可选协议和协议扩展
一小节给出了扩展协议的一个用途。
简单来说就是,从语法上来看,OC
有提供可选协议,但是 Swift
协议中的函数一旦被声明,就要实现它。但是我们可以通过协议扩展,将可选的方法,放在扩展中。
// 摘自 《Swifter-tips》
protocol OptionalProtocol {
func optionalMethod()
func necessaryMethod()
}
extension OptionalProtocol {
func optionalMethod() {
print("Implemented in extension") // 也可以什么都不写
}
}
class MyClass: OptionalProtocol {
func necessaryMethod() {
print("Implemented in Class3")
}
func optionalMethod() {
print("Implemented in Class3")
}
}
关于第二点:
我本来想尝试解释,但是想来想去,总觉的不得要领。详细的还是看下这篇文章(Swift 命名空间形式扩展的实现)的描述,我觉得讲的还是很到位的。
这里摘抄下面这段话来可以作为解答 为什么要用命名空间形式扩展来实现 category 的效果
Objective-C 时代的通行解决办法是在扩展方法名字的最前面加上 XXX_ 形式的前缀。这种形式不但解决了命名冲突的问题,而且增强了代码可读性。一旦阅读到这种风格的方法名,就知道是非系统的实现。Swift 社区最初的一段时间内,也是按照这种命名方式来做的
实际的开发过程中,也经常会对系统库中的已有类型做自定义的扩展,如果有一种通用的形式,来实现这种扩展,那就太好了
注: 这里说的意思,距离说明,假设我有一个扩展字符串的方法,以前的办法是
看完之后,还是觉得有点深奥。以下是我个人的理解:
OC
时代,没有命名空间的概念,所以使用了前缀来实现 category
的区分。swift
时代从语法上增加了命名空间,扩展的实现,不需要增加前缀。但是因为这样的便利,带来了一些别的问题:
举例说明:
如果工程中引用了不同的第三方库,而包括主工程在内,都对
String
做了扩展,扩展的函数名都叫isTestExtensionMethod
, 可能造成调用时产生重叠,无法执行到目标代码。
因此,从形如aString.isTestExtensionMethod()
的调用,改为避免冲突的 形如aString.myCategory.isTestExtensionMethod
.
所以其实讲到这里,才引出这篇文章的标题 —— 命名空间形式扩展
是如何实现的(略啰嗦)
命名空间形式扩展
https://zhang759740844.github.io/2017/11/14/RxSwift%E5%8E%9F%E7%90%86/
网友评论