KnightSama‘s Blog

vuePress-theme-reco KnightSama    2021
KnightSama‘s Blog KnightSama‘s Blog

Choose mode

  • dark
  • auto
  • light
首页
分类
  • iOS
  • 集锦
  • JavaScript
  • Github
  • Python
标签
时间线
GitHub
author-avatar

KnightSama

27

文章

14

标签

首页
分类
  • iOS
  • 集锦
  • JavaScript
  • Github
  • Python
标签
时间线
GitHub
  • Swift 命名空间

    • 三方库中的命名空间
      • RxSwift 中的命名空间
      • KingFisher 中的命名空间
    • 命名空间的原理
      • 命名空间的初步实现
        • 命名空间的最终实现

        Swift 命名空间

        vuePress-theme-reco KnightSama    2021

        Swift 命名空间


        KnightSama 2020-03-19 Swift

        Objective-C 一个一直以来令人诟病的地方就是没有命名空间,在应用开发时,所有的代码和引用的静态库最终都会被编译到同一个域和二进制中。这样的后果是一旦我们有重复的类名的话,就会导致编译时的冲突和失败。为了避免这种事情的发生,Objective-C 的类型一般都会加上两到三个字母的前缀。在 Swift 中,由于可以使用命名空间了,即使是名字相同的类型,只要是来自不同的命名空间的话,都是可以和平共处的。

        # 三方库中的命名空间

        在一些三方库中经常可以看到命名空间的使用

        # RxSwift 中的命名空间

        当我们在使用 RxSwift 时经常用到下面的方法

        let observable = textField.rx.text.orEmpty.asObservable()
        
        1

        其中的 rx 就是 RxSwift 的命名空间

        # KingFisher 中的命名空间

        当我们在使用 KingFisher 时会用到下面的方法

        imageView.kf.setImage(with: URL(string: url))
        
        1

        其中的 kf 就是 KingFisher 的命名空间

        # 命名空间的原理

        如果我们想给 UIView 添加一个 width 属性来获得视图的宽度,我们可以通过 extension 来给 UIView 添加一个名字为 width 的计算属性,这样可以通过下面的方式使用

        let width = self.view.width
        
        1

        但是如果使用了其他的三方库很容易造成冲突,如果按照 Objective-C 的解决思路是添加前缀,这样就变成

        let width = self.view.ks_width
        
        1

        这样虽然可以解决问题但并不优雅,并且也有一定几率重复,我们的目标是变成这样

        let width = self.view.ks.width
        
        1

        ks 就相当于我们的命名空间,这样就防止了冲突,我们应该如何实现呢。通过上面的代码我们可以看出 ks 应该是 view 中的属性,width 是 ks 中的属性,也就是 ks 是个数据结构,里面包含着我们要扩展的属性与方法,同时将这个数据结构作为属性扩展到我们想扩展的结构上去。同时为了让这个数据结构可以访问到我们要扩展的数据结构中的属性与方法,这个数据结构应该包含原始的数据结构。

        # 命名空间的初步实现

        /// 创建命名空间
        struct NameSpace {
            private let view: UIView
            init(view: UIView) {
                self.view = view
            }
        }
        
        /// 为 UIView 添加命名空间
        extension UIView {
            var ks: NameSpace {
                NameSpace(view: self)
            }
        }
        
        /// 给命名空间添加要添加的属性
        extension NameSpace {
            var width: CGFloat {
                self.view.frame.width
            }
        }
        
        /// 使用命名空间
        let width = UIView().ks.width
        
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24

        这样我们就为 UIView 增加了一个命名空间,只需要扩展命名空间的属性与方法就可以了,不过这个实现只能针对 UIView 如果是其他的类型就需要重新设计,解决这个问题只需要将上面的命名空间改为泛型版本

        /// 创建泛型命名空间
        struct NameSpace<Base> {
            private let base: Base
            
            init(base: Base) {
                self.base = base
            }
        }
        
        /// 为 UIView 添加命名空间
        extension UIView {
            var ks: NameSpace<UIView> {
                NameSpace(base: self)
            }
        }
        
        /// 给命名空间添加要添加的属性
        extension NameSpace where Base: UIView {
            var width: CGFloat {
                self.base.frame.width
            }
        }
        
        /// 使用命名空间
        let width = UIView().ks.width
        
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25

        这样我们实现了一个简单的泛型命名空间,实际上我们发现为 UIView 添加命名空间的过程对任意数据结构都相同,我们可以通过构建一个协议来简化这一过程,同时上面的实现没有考虑到静态属性与方法的扩展问题。

        # 命名空间的最终实现

        /// 创建泛型命名空间
        struct NameSpace<Base> {
            private let base: Base
            
            init(base: Base) {
                self.base = base
            }
        }
        
        /// 创建泛型协议
        protocol NameSpaceProtocol {
            /// 关联类型
            associatedtype ProtocolType
            /// 实例调用
            var ks: ProtocolType {get}
            /// 静态调用
            static var ks: ProtocolType.Type {get}
        }
        
        /// 默认实现泛型协议
        extension NameSpaceProtocol {
            
            var ks: NameSpace<Self> {
                NameSpace<Self>(base: self)
            }
            
            static var ks: NameSpace<Self>.Type {
                NameSpace<Self>.self
            }
            
        }
        
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31

        命名空间的使用

        /// 为 UIView 添加命名空间
        extension UIView: NameSpaceProtocol {}
        
        /// 给命名空间添加要添加的属性或方法
        extension NameSpace where Base: UIView {
            var width: CGFloat {
                self.base.frame.width
            }
            
            static func test() {
                print("测试静态方法")
            }
        }
        
        /// 使用命名空间
        let width = UIView().ks.width
        
        UIView.ks.test()
        
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        欢迎来到 KnightSama‘s Blog
        看板娘