美文网首页iOS进阶指南Swift 专栏Swift学习
Swift 命名空间形式扩展的理解和问题探讨

Swift 命名空间形式扩展的理解和问题探讨

作者: 黑羽肃霜 | 来源:发表于2019-02-12 12:35 被阅读9次

    先从 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 中实际上是可以不去写 method1method2 的实现的。因为协议扩展中已经给出了它们的基本实现
    • sp 调用结果为什么不同?
      如代码中声明,这里的 sS 这个结构体的实例, p 实际上是某个遵循 P 协议的结构体/类的实例。
      因此,在实际调用时,s 很明确的表明了,自己是 S 这个结构体下面的,而 p 我不知道我到底是谁家的,那么我只能用协议默认的了。
      s 的这种具体实现的情况,叫做 swift 中的动态派发(我应该没有说错)

      关于动态派发,有一篇 参考文章


    协议扩展解决了什么问题

    • Swift 协议中怎么实现 OC 协议中的 @option 功能
    • 实现类似于 OCcategory的效果,在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/

    相关文章

      网友评论

        本文标题:Swift 命名空间形式扩展的理解和问题探讨

        本文链接:https://www.haomeiwen.com/subject/yqoheqtx.html