Swift - 簡(jiǎn)單Router

router工具,屬于項(xiàng)目的基礎(chǔ)工具類之一煞茫,也查閱了一些第三方庫(kù)帕涌,不過(guò)都不是特別滿意,我就嘗試著自己簡(jiǎn)單封裝了一個(gè)续徽。

先說(shuō)下思路蚓曼,因與后臺(tái)、安卓钦扭、前端人員溝通纫版,依舊采用key, value的形式來(lái)處理跳轉(zhuǎn),比如客情,后臺(tái)給個(gè)

    let dic = ["target":"setting","para":["testKey":"123"]]

這種結(jié)果的json其弊,這里的target,就是約定的key膀斋,用來(lái)指定某一頁(yè)面梭伐,后面的para代表傳遞的參數(shù),如果想OC那樣仰担,用plist表來(lái)記錄與維護(hù)的話糊识,雖然也能實(shí)現(xiàn),但是在自己想用這種方式的跳轉(zhuǎn)的時(shí)候惰匙,就會(huì)因?yàn)檫@里的target沒(méi)提示技掏,而會(huì)有寫錯(cuò)的可能性,所以這里采用枚舉的方式實(shí)現(xiàn)项鬼。如下:

enum RouterType: String {
    typealias RawValue = String
    
    case setting
    case accountSafe
    case webView
    
    func getController() -> BaseViewController {
        switch self {
        case .setting:
            return UserSettingViewController.init()
        case .accountSafe:
            return AccountSafeVC.init()
        default:
            return BaseViewController.init()
        }
    }
}

這樣哑梳,在使用的時(shí)候,直接提示出來(lái)绘盟,而不會(huì)因?yàn)槿亲址磳戝e(cuò)誤鸠真。

let target = RouterType.setting.getController()

然后就是賦值,開(kāi)始時(shí)龄毡,會(huì)很自然的想到kvc來(lái)賦值

let vcKeys = RouterUtil.getAllPropertys(vc)
para.allKeys().forEach { key in
            if vcKeys.contains(key) {
                // 有這個(gè)值
                let value = para[key]
                // MARK: 該方法需要vc實(shí)現(xiàn) setValue:forUndefinedKey: 方法
                vc.setValue(value, forKeyPath: key)
            }
        }

這里的allKeys與getAllPropertys是我封裝的小工具吠卷,allkeys,就是字典的所有key沦零,而getAllPropertys是利用反射Mirror祭隔,來(lái)獲取對(duì)應(yīng)vc的屬性,在一一賦值路操。
可是這里需要注意:使用kvc 需要目標(biāo)vc實(shí)現(xiàn)

 setValue:forUndefinedKey: 

方法疾渴,且被賦值的屬性前千贯,需要加上@objc的標(biāo)識(shí)

@objc var testKey:String?

而且類型要一致,才能賦值成功搞坝,雖然可以使用基類來(lái)實(shí)現(xiàn)setValue:forUndefinedKey: 但是搔谴,每個(gè)屬性前都需要加上@objc就會(huì)很麻煩,而且如果哪天后臺(tái)傳來(lái)的參數(shù)中桩撮,類型不對(duì)了敦第,也會(huì)導(dǎo)致賦值失敗,這是我們不想看到的店量。這時(shí)芜果,我就想起來(lái)了HandyJSON框架,它就可以不用加@objc垫桂,然后正確的給屬性賦值师幕,雖然已經(jīng)不維護(hù)了,但是我還是決定去看看它的實(shí)現(xiàn)原理诬滩,然后看看能不能借鑒霹粥。

在查閱了相關(guān)資料后,我們得知疼鸟,HandyJSON其實(shí)是通過(guò)操作內(nèi)存的方式來(lái)完成賦值的后控。

在HandyJSON中,不管是class空镜,還是struct浩淘,在賦值時(shí),都會(huì)進(jìn)入下面這個(gè)方法

static func _transform(dict: [String: Any], to instance: inout Self) 

在這個(gè)方法中會(huì)有兩個(gè)結(jié)構(gòu)體需要提前看一下:

struct Property {
    let key: String
    let value: Any

    /// An instance property description
    struct Description {
        /// key名稱
        public let key: String
        /// 類型
        public let type: Any.Type
        /// 屬性所需內(nèi)存大小
        public let offset: Int
        public func write(_ value: Any, to storage: UnsafeMutableRawPointer) {
            return extensions(of: type).write(value, to: storage.advanced(by: offset))
        }
    }
}

詳情

struct PropertyInfo {
    let key: String
    let type: Any.Type
  // 內(nèi)存地址
    let address: UnsafeMutableRawPointer
    let bridged: Bool
}

下面仔細(xì)看該方法:

static func _transform(dict: [String: Any], to instance: inout Self) {
        /// 獲取類型的屬性列表 包含 每個(gè)屬性的內(nèi)存大小
        guard let properties = getProperties(forType: Self.self) else {
            InternalLogger.logDebug("Failed when try to get properties from type: \(type(of: Self.self))")
            return
        }

        // do user-specified mapping first
        let mapper = HelpingMapper()
        instance.mapping(mapper: mapper)

        // get head addr 對(duì)象的Head節(jié)點(diǎn)
        let rawPointer = instance.headPointer()
        InternalLogger.logVerbose("instance start at: ", Int(bitPattern: rawPointer))

        // process dictionary 對(duì)dict進(jìn)行格式化
        let _dict = convertKeyIfNeeded(dict: dict)

        // 判斷是否是OC對(duì)象
        let instanceIsNsObject = instance.isNSObjectType()
        // 橋接屬性
        let bridgedPropertyList = instance.getBridgedPropertyList()
        
        // 循環(huán)properties數(shù)組
        for property in properties {
            let isBridgedProperty = instanceIsNsObject && bridgedPropertyList.contains(property.key)
            
            // 屬性的地址
            let propAddr = rawPointer.advanced(by: property.offset)
            InternalLogger.logVerbose(property.key, "address at: ", Int(bitPattern: propAddr))
            if mapper.propertyExcluded(key: Int(bitPattern: propAddr)) {
                InternalLogger.logDebug("Exclude property: \(property.key)")
                continue
            }
            
            /// 屬性信息結(jié)構(gòu)體
            let propertyDetail = PropertyInfo(key: property.key, type: property.type, address: propAddr, bridged: isBridgedProperty)
            InternalLogger.logVerbose("field: ", property.key, "  offset: ", property.offset, "  isBridgeProperty: ", isBridgedProperty)
            
            /// 獲取對(duì)應(yīng)屬性值
            if let rawValue = getRawValueFrom(dict: _dict, property: propertyDetail, mapper: mapper) {
                /// 轉(zhuǎn)換成需要的類型
                if let convertedValue = convertValue(rawValue: rawValue, property: propertyDetail, mapper: mapper) {
                    /// 進(jìn)行賦值
                    assignProperty(convertedValue: convertedValue, instance: instance, property: propertyDetail)
                    continue
                }
            }
            InternalLogger.logDebug("Property: \(property.key) hasn't been written in")
        }
    }

這里在HandyJSON中吴攒,引用了Reflection https://github.com/Zewo/Reflection 用來(lái)實(shí)現(xiàn)類屬性解析的功能张抄。

看到這里,基本上就找到了 我們處理后臺(tái)路由傳值的方案了洼怔,所以我們項(xiàng)目的RouterUtil基礎(chǔ)版就誕生了署惯。
使用枚舉,通過(guò)string來(lái)得到對(duì)應(yīng)的VC镣隶,然后在通過(guò)這種方式進(jìn)行賦值极谊。賦值過(guò)程如下:

static func transform(dict: [String: Any], to instance: UIViewController) {
        let type = type(of: instance)
        /// 獲取類型所有的屬性
        guard let properties = getProperties(forType: type) else {
            return
        }
        
        /// 獲取對(duì)象內(nèi)存中的head節(jié)點(diǎn)
        let rawPointer = instance.headPointerOfClass()
        
        properties.forEach { property in
            /// 屬性在內(nèi)存中的地址
            let propAddr = rawPointer.advanced(by: property.offset)
            
            let detail = PropertyInfo(key: property.key, type: property.type, address: propAddr)
            
            /// 從字典中獲取對(duì)應(yīng)值
            if let rawValue = dict[property.key] {
                /// 把值轉(zhuǎn)成需要的類型,比如 需要string安岂,后臺(tái)給了Int 直接賦值會(huì)失敗轻猖,需要轉(zhuǎn)成string
                if let convertedValue = convertValue(rawValue: rawValue, property: detail) {
                    /// 寫入對(duì)應(yīng)內(nèi)存地址內(nèi)
                    extensions(of: property.type).write(convertedValue, to: detail.address)
                }
            }
        }
    }

轉(zhuǎn)屬性的方法,我借鑒了HandyJSON庫(kù)里面的方式域那,并進(jìn)行了改造

   fileprivate func convertValue(rawValue: Any, property: PropertyInfo) -> Any? {
    
    /// 需要轉(zhuǎn)化時(shí)
    if let transformType = property.type as? BuiltInBasicTypeProtocol.Type {
        /// 轉(zhuǎn)換結(jié)果咙边, 比如需要Int,但是后臺(tái)給了string
        return transformType.transform(from: rawValue)
    } else {
        return extensions(of: property.type).takeValue(from: rawValue)
    }
}

因?yàn)镠andyJSON需要類遵守HandyJSON協(xié)議,而败许,我們這里只需要被賦值的屬性遵守BuiltInBasicTypeProtocol協(xié)議就可以了友瘤。在BuiltInBasicTypeProtocol協(xié)議中,對(duì)基礎(chǔ)屬性進(jìn)行了擴(kuò)展檐束。這里就不詳細(xì)列出了,感興趣可以查看源碼束倍。

使用起來(lái)也比較簡(jiǎn)單

RouterUtil.pushVC(target: "setting", params: ["testKey":123123])

github地址
https://github.com/michael0217/SwiftRouterDemo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末被丧,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子绪妹,更是在濱河造成了極大的恐慌甥桂,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件邮旷,死亡現(xiàn)場(chǎng)離奇詭異黄选,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)婶肩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門办陷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人律歼,你說(shuō)我怎么就攤上這事民镜。” “怎么了险毁?”我有些...
    開(kāi)封第一講書人閱讀 158,369評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵制圈,是天一觀的道長(zhǎng)膨报。 經(jīng)常有香客問(wèn)我拭荤,道長(zhǎng)汁胆,這世上最難降的妖魔是什么分唾? 我笑而不...
    開(kāi)封第一講書人閱讀 56,799評(píng)論 1 285
  • 正文 為了忘掉前任强法,我火速辦了婚禮锻狗,結(jié)果婚禮上辐啄,老公的妹妹穿的比我還像新娘息尺。我一直安慰自己域庇,他們只是感情好嵌戈,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,910評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著听皿,像睡著了一般熟呛。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上尉姨,一...
    開(kāi)封第一講書人閱讀 50,096評(píng)論 1 291
  • 那天庵朝,我揣著相機(jī)與錄音,去河邊找鬼。 笑死九府,一個(gè)胖子當(dāng)著我的面吹牛椎瘟,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播侄旬,決...
    沈念sama閱讀 39,159評(píng)論 3 411
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼肺蔚,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了儡羔?” 一聲冷哼從身側(cè)響起宣羊,我...
    開(kāi)封第一講書人閱讀 37,917評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎汰蜘,沒(méi)想到半個(gè)月后仇冯,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,360評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡族操,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,673評(píng)論 2 327
  • 正文 我和宋清朗相戀三年苛坚,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片色难。...
    茶點(diǎn)故事閱讀 38,814評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡泼舱,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出莱预,到底是詐尸還是另有隱情柠掂,我是刑警寧澤,帶...
    沈念sama閱讀 34,509評(píng)論 4 334
  • 正文 年R本政府宣布依沮,位于F島的核電站涯贞,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏危喉。R本人自食惡果不足惜宋渔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,156評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望辜限。 院中可真熱鬧皇拣,春花似錦、人聲如沸薄嫡。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)毫深。三九已至吩坝,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間哑蔫,已是汗流浹背钉寝。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,123評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工弧呐, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人嵌纲。 一個(gè)月前我還...
    沈念sama閱讀 46,641評(píng)論 2 362
  • 正文 我出身青樓俘枫,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親逮走。 傳聞我的和親對(duì)象是個(gè)殘疾皇子鸠蚪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,728評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容