Swift extension
Extensions in Swift can:
- Add computed instance properties and computed type properties
- Define instance methods and type methods
- Provide new initializers
- Define subscripts
- Define and use new nested types
- Make an existing type conform to a protocol
1 - 4, 统一表示可以添加方法, 属性等
5, 可以定义一些嵌套的类型
6, 给当前类型实现一些新的协议
下面这部分代码包含了 1 - 6 的具体实现, 具体就不说了
struct A {}
struct B {
func run() {
print("run")
}
}
protocol C {
func ccc()
}
extension A: C {
static var b = B()
var bb: B {
get {
Self.b
}
set {
Self.b = newValue
}
}
func run() {
Self.b.run()
}
static func run() {
b.run()
}
init(b: B) {
Self.b = b
}
struct AA {}
func ccc() {
}
}
print(A.b.run())
Swift extension 包含泛型的类型 where 判断 T 是否实现某个协议
struct K: Print {
func print() {
Swift.print("KKKKKK")
}
}
struct KK {}
struct B<T> {
var t: T
init(t: T) {
self.t = t
}
}
extension B where T: Print {
func printT() {
self.t.print()
}
}
let b = B(t: KK())
let b1 = B(t: K())
b1.printT()
如上代码, 只有传入的 T 实现了 Print 协议, B才包含 printT() 方法.
当传入的是 KK() 时, 则无法调用 printT()
这里判断 T 是否实现某个协议使用的是 :
Swift extension 包含泛型的类型 where 判断 T 是否是某个类型
struct B<T> {
var t: T
init(t: T) {
self.t = t
}
}
class CC {}
class CCC: CC {}
extension B where T == CC {
func printT() {
print("CCC")
}
}
let b2 = B(t: CC())
b2.printT()
判断 T 是否是某个类型用 ==
, ==
判断的是绝对的类型, T == CC, 即使你传入的是该类型的子类也不可以, let b2 = B(t: CCC())
, b2 是没有 printT() 方法的.
protocol extension
- 添加协议中方法的默认实现
- 添加一些新的方法或者属性并实现, 和其他的扩展相比, 不可以添加新的协议的继承, 不可以定义一些嵌套的类型.
- 给这个协议添加一些约束, 如下代码, 只有当泛型 Element 实现了
Equatable
协议, 才给Collection
协议, 添加allEqual()
方法
extension Collection where Element: Equatable {
func allEqual() -> Bool {
for element in self {
if element != self.first {
return false
}
}
return true
}
}
- 关于 where 后面加的是
==
类型判断的是使用, 和上面的extension
中介绍的相同
主要是协议中定义的泛型的类型约束, 可以使用:
来进行协议的约束, 使用==
来进行具体类型的约束
extension 关于静态派发
先定义两个协议 A1、A2,都定义了 protocolRun 方法,在 extension 中提供了默认实现,并且在 extension 中额外给两个协议增加了 extensionRun 方法。
protocol A1 {
func protocolRun()
}
extension A1 {
func protocolRun() {
print("protocolRun A1")
}
func extensionRun() {
print("extensionRun A1")
}
}
protocol A2 {
func protocolRun()
}
extension A2 {
func protocolRun() {
print("protocolRun A2")
}
func extensionRun() {
print("extensionRun A2")
}
}
如下,让 AA 实现了协议 A1、A2,但是由于 A1,A2 都定义了协议 protocolRun,即使在 extension 中提供默认实现,编译器还是会报错,必须实现 protocolRun 方法,因为 AA,无法确定改用哪个默认实现。
class AA: A1, A2 {
}
// 编译报错
// Type 'AA' does not conform to protocol 'A1'
// Type 'AA' does not conform to protocol 'A2'
接下来,AA 实现了 protocolRun,编译运行,没有问题。
class AA: A1, A2 {
func protocolRun() {
print("protocolRun AA")
}
}
let aa = AA()
aa.protocolRun()
// 打印
// protocolRun AA
接下来,我们调用 extensionRun 方法,编译直接报错 Ambiguous use of 'extensionRun()', 为什么这里只有在调用的时候才报编译错误那,它和 protocolRun 的区别是什么?
class AA: A1, A2 {
func protocolRun() {
print("protocolRun AA")
}
}
let aa = AA()
aa.extensionRun() // Ambiguous use of 'extensionRun()'
根据上面的经验,给 AA 也提供了 extensionRun 的实现,ok, 看起来没问题了。
class AA: A1, A2 {
func protocolRun() {
print("protocolRun AA")
}
func extensionRun() {
print("extensionRun AA")
}
}
let aa = AA()
aa.extensionRun()
// 打印
// extensionRun AA
但根据上面 extensionRun 和 protocolRun 两个方法编译时表现的不同,它们总是有差异的。
协议动态派发和静态派发:
swift protocol 定义的方法走的是表派发,即动态派发,调用方法时先去协议表中查找方法地址。
extension 添加的方法走的是静态派发,静态派发在编译时,调用方法的位置直接执行方法地址。
protocol Drawable {}
struct Point: Drawable {}
struct Line: Drawable {}
struct Pair {
init(_ a: Drawable, _ b: Drawable) {
self.first = a
self.second = b
}
var first: Drawable
var second: Drawable
}
Generic Code 和 protocol 的区别
泛型可以优化编译,在编译过程中直接转化为静态派发
// 方法 1
func pFunc(a: Drawable) {
}
// 方法2
func gFunc<T: Drawable>(b: T) {
}
方法2在编译的时候,就会吧 T 替换成具体的类型, 例如:
gFunc(b: Line())
会被编译成下面这样,这样就去除了 protocol 表的使用
func gFunc<T: Line>(b: Line) {
}
所以泛型在编译的过程中,会根据实际的调用生成不同的方法
protocol 是如何存放到数组中的?
existential container
前三个值留个 valueBuffer
小于3个值,直接存放到 valueBuffer
大于3个值的时候,存放一个指针指向堆 alloc
https://developer.apple.com/videos/play/wwdc2016/416/?time=1474
https://devstreaming-cdn.apple.com/videos/wwdc/2016/416k7f0xkmz28rvlvwb/416/416_understanding_swift_performance.pdf?dl=1