Swift 优雅的前缀

作者: long弟弟 | 来源:发表于2021-07-11 17:22 被阅读0次

    闷热的周末,午后

    • 朋友的自拍.jpg

    从OC转Swift不再需要添加类前缀了,如OC的PMHomeViewController转到Swift可以命名为HomeViewController。因为Swift有命名空间的概念了。不用担心与系统或者第三方命名。如果创建的类和系统的类重名,调用自己的直接调用即可,系统的类则需要加上相应的框架名字,如

    import UIKit
    class UIView {
        
    }
    class ViewController: UIViewController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
            
            let v1 = UIKit.UIView()
            let v2 = Project.UIView() //项目名称或说Target为Project,通常被省略
            print("v1:\(v1)")
            print("v2:\(v2)")
        }
    }
    //v1:<UIView: 0x7ff24e40a830; frame = (0 0; 0 0); layer = <CALayer: 0x6000027f2a80>>
    //v2:Project.UIView
    

    此处Project就有命名空间的意思了

    本文借鉴Swift编程从入门到精通-MJ大神精选课程中的一节,将加前缀的推导过程整理并加深自己的理解。看过的朋友可跳过。

    假设有个字符串,需要计算包含的数字个数。通常的做法是写个方法传递该字符串,返回数字的个数,如:

    //计算字符串里的数字
    let str = "123test123" 
    
     func numberCount(_ str: String) -> Int {
        var count = 0
        for c in str {
            if ("0"..."9").contains(c) {
                count += 1
            }
        }
        return count
     }
    

    优化成Swift 风格

     func numberCount(_ str: String) -> Int {
        var count = 0
        for c in str where ("0"..."9").contains(c) {
            count += 1
        }
        return count
     }
    

    可以直接调用

    print("数字count", numberCount(str))
    

    一般情况,写的是字符串相关的方法,会扩充到相应类/结构体的扩展extension

     extension String {
         //给字符串扩展功能
         func numberCount() -> Int {
             var count = 0
             for c in self where ("0"..."9").contains(c) {
                 count += 1
             }
             return count
         }
     }
     print(str.numberCount()) //可以使用str直接调用改方法
    

    调方法有小括号,可以改为计算属性,去掉小括号

     extension String {
         //改为计算属性
         var numberCount: Int {
             var count = 0
             for c in self where ("0"..."9").contains(c) {
                 count += 1
             }
             return count
         }
     }
     print(str.numberCount)
    

    计算属性可能跟系统自带的产生冲突

    解决办法

    1. 仿照OC写前缀
    var pm_numberCount: Int {
         var count = 0
         for c in self where ("0"..."9").contains(c) {
            count += 1
         }
         return count
      }
     print(str.pm_numberCount)
    
    1. 仿照RxSwift,给str添加前缀,实现:str.pm.numberCount类似的效果
     print(str.pm.numberCount)
    

    扩展.pm属性,然后在PM中扩展方法

    extension String {
        var pm: PM {
            //返回PM初始化话,传入当前字符串
            return PM(self)
        }
    }
    
    struct PM {
        //属性列表
        var string: String = ""
        //初始化
        init(_ string: String) {
            self.string = string
        }
        //给字符串扩充方法
        var numberCount: Int {
            var count = 0
            for c in self.string where ("0"..."9").contains(c) {
                count += 1
            }
            return count
        }
    }
    

    不够通用,给字符串扩充功能,给数组呢,都要添加属性列表、初始化一遍?考虑泛型,扩展方法列表都扩展.pm属性。

    struct PM<Base> {
        //属性列表
        var base: Base
        //初始化
        init(_ base: Base) {
            self.base = base
        }
    }
    
    extension String {
        var pm: PM<String> {
            PM(self)
        }
    }
    

    然后给PM扩展方法

     //扩展 PM(self) base就是字符串,Base是泛型String类型
     extension PM where Base == String {
         var numberCount: Int {
             var count = 0
             for c in base where ("0"..."9").contains(c) {
                 count += 1
             }
             return count
         }
     }
    

    这样也可以给自定义的类扩充.pm属性

    class PMPerson {}
    extension PMPerson {
        var pm: PM<PMPerson> {
            //Base就是PMPerson
            PM(self)
        }
    }
     extension PM where Base == PMPerson { //==只是Person类,: Person及其子类
         func test() {
             print("test--")
         }
     }
    

    再进一步优化,不仅给成员属性增加,给类属性也增加.pm属性,实现类似String.pm.numberCount

    extension String {
        var pm: PM<String> {
            PM(self)
        }
        static var pm: PM<String>.Type {
            PM<String>.self
        }
    }
    extension PM where Base == String {
         var numberCount: Int {
             var count = 0
             for c in base where ("0"..."9").contains(c) {
                 count += 1
             }
             return count
         }
         static func Stringtest() {
             print("Stringtest")
         }
     }
    

    这样也实现了类属性增加.pm属性,实现String.pm.Stringtest()
    这样以后自定义的类都可以像上述那样,增加成员属性、类属性.pm,并扩充方法,如下Dog

    class Dog{ }
    extension Dog {
        var pm: PM<Dog> {
            PM(self)
        }
        static var pm: PM<Dog>.Type {
            PM<Dog>.self
        }
    }
    extension PM where Base == Dog {
        func dogf1() {
            
        }
        static func dogf2() {
            
        }
    }
    

    后续有想添加.pm属性的,都可以这样扩展。但相同的部分,可用协议优化

    protocol PMCompatible {}
    extension PMCompatible {
        var pm: PM<Self> {
            set{} //mutating能编译过
            get{PM(self)}
        }
        static var pm: PM<Self>.Type {
            set{}
            get{PM<Self>.self}
        }
    }
    

    只要让类遵守该PMCompatible协议,就有.pm属性,继续优化

    class Dog: PMCompatible { }
    extension PM where Base == Dog {
        mutating func eat() {
            print("eat")
        }
    }
    

    不管字符串是String还是NSString/NSMutableString,都遵守该协议

    extension String: PMCompatible {}
    extension NSString: PMCompatible {}
    //扩展PM遵守字面量协议
    extension PM where Base: ExpressibleByStringLiteral {
       static var random10String: String {
            let letters = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
            var randomString = ""
            for _ in 0..<10 {
                let ch = letters.randomElement()
                randomString.append(ch!)
            }
            return randomString
        }
    }
    

    以上优雅的实现了
    String.pm.random10String:随机产生10个字符串
    str.pm.numberCount:计算字符串里数字的个数

    核心代码

    struct PM<Base> {
        var base: Base
        init(_ base: Base) {
            self.base = base
        }
    }
    
    protocol PMCompatible {}
    extension PMCompatible {
        var pm: PM<Self> {
            set{} //mutating能编译过
            get{PM(self)}
        }
        static var pm: PM<Self>.Type {
            set{}
            get{PM<Self>.self}
        }
    }
    

    最后,要给哪个类/结构体/枚举如XXX扩展.pm属性,只需遵守PMCompatible协议并扩充extension PM where Base == XXX即可

    struct XXX { }
    extension XXX: PMCompatible { }
    extension PM where Base == XXX {
      //扩充方法或属性
      func test1() {
      }
      static func test1 {
      }
      
    }
    //XXX.pm.test1()
    //XXX().pm.test1()
    

    相关文章

      网友评论

        本文标题:Swift 优雅的前缀

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