Swift-Reflection

5830 ワード

前言
Javaに詳しい読者は反射(Reflection)を知っているかもしれません.これは、実行時にタイプの動作を検出、アクセス、または変更する特性です.一般的な静的言語タイプの構造やメソッドの呼び出しなどはコンパイル時に決定する必要があり,開発者ができることはifやswitchなどの制御フローだけを用いてどのような設定や呼び出しを行うかを決定することが多い.反射特性は、実行時にいくつかの条件で呼び出しの方法をリアルタイムで決定したり、あるタイプに属性や方法を動的に設定したりする機会を与えることができ、非常に柔軟で強力な言語特性です.
Objective-Cでは、Objective-Cの動作時が一般的な反射よりも柔軟で強力であるため、「反射」という言葉はあまり言及されません.文字列によってクラスやselectorを生成し、さらにオブジェクトや呼び出し方法を生成するなど、反射の具体的な表現に慣れている読者も多いかもしれません.一方,SwiftではObjective-Cの実行時の部分を捨てても,純粋なSwiftの範疇には反射に関するいくつかの内容が存在するが,相対的に機能が弱い.
『Swifter:100個のSwift開発必須Tip』より抜粋
需要
  • 取得クラスの属性リスト
  • 属性付与
  • Swift反射の実現方式
  • Mirror
  • OC runtime

  • 方法一:Mirror
    インプリメンテーションモード
    属性のタイプの取得
    func getTypeOfProperty (_ name: String) -> Any.Type {
        //   :self   (  ),    ,        
        var type: Mirror = Mirror(reflecting: self)
        for child in type.children {
            if child.label! == name {
               return  type(of: child.value)
            }
        }
        while let parent = type.superclassMirror {
            for child in parent.children {
                if child.label! == name {
                    return type(of: child.value)
                }
            }
            type = parent
        }
        return NSNull.Type.self
    }
    

    属性に値を割り当てる
    func setObjectParams(obj: NSObject, paramsDic:[String:Any]?) {
        if let paramsDic = paramsDic {
            for (key,value) in paramsDic {
                let type = obj.getTypeOfProperty(key)
                if type == NSNull.Type.self {
                    print("[\(obj)]  [\(key)]  ")
                }else if …… {
                    // ……
                }else {
                    obj.setValue(value, forKey: key)
                }
            }
        }
    }
    

    特長
  • 単純(Swiftのすべてのタイプが_Reflectableを実現)
  • はタイプの属性を取得できず、インスタンスの属性
  • のみを取得できる.
  • は、getまたはsetと書かれたメソッドの属性
  • を取得できない.
    方式2:OC runtime
    インプリメンテーション方式(部分コード)
    属性のタイプの取得
    ///           
    /// -   :        ,          ,      ,       !
    /// -    :https://github.com/Sajjon/SwiftReflection
    ///
    /// - parameter clazz:             
    ///
    /// - returns:     &        .
    open class func propertyList(clazz: NSObject.Type) -> [[String: Any]]? {
        var count: UInt32 = 0
        let list = class_copyPropertyList(clazz, &count)
        var resultList = [[String: Any]]()
        for i in 0..

    属性に値を割り当てる
    ///      
    ///
    /// - parameter paramsDict:             
    /// - parameter obj:                      
    /// - parameter complete:                  
    open class func setParams(_ paramsDict:[String:Any]?, for obj: NSObject, complete: (()->()) = {}) {
        if let paramsDict = paramsDict {
            let clazz: NSObject.Type = type(of: obj)
            let list = propertyList(clazz: clazz) ?? []
            var filteredList = [[String: Any]]()
            let _ = paramsDict.map({ dict in
                let tmp = list.filter({ $0.keys.contains(dict.key)}).first ?? [:]
                filteredList.append(tmp)
            })
            print("=================      =================")
            for (key, value) in paramsDict {
                //   key     
                let value = "\(value)"
                let type = getType(key: key, typeDictList: filteredList)
                if InnerConst.BOOL == type {
                    let toValue = value.toBool() ?? false
                    obj.setValue(toValue, forKey: key)
                } else if …… {
                    // ……
                } else if InnerConst.NULL == type {
                    print("[\(obj)]  [\(key)]  ")
                }
            }
            print("=================      =================")
            complete()
        }
    }
    

    特長
  • は、クラスまたはインスタンス
  • に作用する強力な機能を備えています.
  • は面倒で、Objective-Cの基礎
  • が必要です.
  • 初期化されていない非参照タイプ属性を取得できません(値タイプは初期化する必要があります!)
  • が作用するオブジェクトは、NSObject
  • から継承する必要があります.
    使用法(ユニットテスト添付)
    属性リストの取得
    func testGetPropertyList() {
        if let list = SwiftReflectionTool.propertyList(clazz: Book.self) {
            for dict in list {
                for (value, type) in dict {
                    print("\(value) : \(type)")
                }
            }
        } else {
            print("        !")
        }
    }
    

    静的属性に値を割り当てる(KVC)
    func testSetParams4StaticProperty() {
        print("==============            ==============")
        print("   :count = \(Book.value(forKey: InnerConst.CountKey) ?? "")")
        Book.setValue(2, forKey: InnerConst.CountKey)
        print("   :count = \(Book.value(forKey: InnerConst.CountKey) ?? "")")
    }
    

    インスタンス属性に値を割り当てる
    func testSetParams4InstanceProperty() {
        print("==============            ==============")
        let paramsDict = [
            "title": "XXX      ",
            "author": "cy",
            "numberOfPages": 250,
            "released": "20170707",
            "isSaled": true
        ] as [String : Any]
        let book = Book(title: "", author: "", numberOfPages: 0, released: Date(), isSaled: false)
        print("   :")
        printProperties(obj: book)
        SwiftReflectionTool.setParams(paramsDict, for: book) { 
            print("    ")
        }
        print("   :")
        printProperties(obj: book)
    }
    //     
    private func printProperties(obj: NSObject) {
        if let list = SwiftReflectionTool.propertyList(clazz: type(of: obj)) {
            for dict in list {
                for (value, _) in dict {
                    print("\(value) : \(obj.value(forKey: value) ?? "")")
                }
            }
        } else {
            print("        !")
        }
    }