swift中做KVC優(yōu)化的心得

  • 寫在前面

我并沒(méi)有專門寫一個(gè)swift下KVC的開源庫(kù)脸甘,伸手黨們可以command+W了
我做的優(yōu)化是針對(duì)自己寫的一個(gè)相對(duì)比較重的MVC庫(kù)里的KVC優(yōu)化析苫,之前寫的太簡(jiǎn)單了黔帕。

  • 進(jìn)入正題

優(yōu)化之前的KVC代碼:

func autoKVCBinding(_ dictionary:Dictionary<String, Any>?) {
        if dictionary != nil {
            let mirror:Mirror = Mirror(reflecting: self);
            for (label,value) in mirror.children {
                if label != nil && dictionary![label!] != nil {
                    if value is LBItem && dictionary![label!] is Dictionary<String, Any> {
                        let subItem = value as! LBItem;
                        subItem.autoKVCBinding(dictionary![label!] as? Dictionary<String, Any>);
                    } else {
                        self.setValue(dictionary![label!], forKey: label!);
                    }
                }
            }
        }
    }

功能很簡(jiǎn)單冲泥,單純只做了空值校驗(yàn)氧猬,property如果還是同名model的話可以做遞歸的KVC校驗(yàn)妈嘹。

  • 那么柳琢,第一個(gè)問(wèn)題,我們要做的是什么润脸?

通俗點(diǎn)說(shuō)就是我們需要做上哪些新功能柬脸,達(dá)到哪些目標(biāo)。
這是我在動(dòng)手之前思考的一個(gè)問(wèn)題毙驯,先明確需求倒堕,要做哪些新功能,有目標(biāo)的去找解決方案尔苦。
這方面我列了幾條

  1. 解決類型不同的轉(zhuǎn)換問(wèn)題
  2. 解決泛型Array的賦值問(wèn)題(Array<XXModel>)
  3. 父類和父類的父類賦值問(wèn)題涩馆,往前遞歸幾層行施?是否做限制?
  4. 過(guò)濾Foundation框架里的屬性

明確了問(wèn)題之后就可以找解決方案了魂那,首先在OC下由于有強(qiáng)大牛逼的runtime和完善的Foundation的API蛾号,這幾個(gè)問(wèn)題都不是問(wèn)題,不過(guò)還是記錄一下吧:

  1. 類型不同的轉(zhuǎn)換問(wèn)題:解決這個(gè)問(wèn)題要單獨(dú)把不同類型拉出來(lái)涯雅,如果是數(shù)字類型轉(zhuǎn)string鲜结,可以直接取stringValue,也可以直接stringWithFormat活逆,如果是string轉(zhuǎn)數(shù)字類型精刷,則可以統(tǒng)一用NSNumberFormatter處理,轉(zhuǎn)成NSNumber即可
  2. 泛型Array的賦值問(wèn)題蔗候,runtime可以上場(chǎng)了怒允,先獲取當(dāng)前class的所有屬性,拿到objc_property_t類型锈遥,property_getAttributes這個(gè)API拿到的字符串中是包含NSArray<XXXModel>這種泛型字段的纫事,直接把NSArray的元素類型截出來(lái),再轉(zhuǎn)換成Class對(duì)象就可以了所灸。
  3. 父類的問(wèn)題丽惶,這里我思考了很久,網(wǎng)上幾個(gè)庫(kù)的解決方案不一樣爬立,有的是直接遞歸到NSObject層钾唬,再在屬性篩選的時(shí)候把系統(tǒng)API給去掉,有的是在遞歸的時(shí)候加個(gè)判斷侠驯,遞歸到Foundation的時(shí)候就break抡秆,我比較了一下覺(jué)得后者性能更好,減少了遞歸次數(shù)陵霉,但是沒(méi)有實(shí)驗(yàn)的地方是如果子類override了Foundation框架中的屬性會(huì)怎么樣琅轧,不過(guò)這么做的情況也很少,我也就暫時(shí)忽略掉了踊挠。
  4. Foundation屬性的過(guò)濾乍桂,這個(gè)在上一層父類遞歸的時(shí)候就已經(jīng)篩選掉了,因?yàn)椴粫?huì)遞歸到Foundation的類上面去效床,自然也不會(huì)出現(xiàn)Foundation的屬性睹酌。

剛才說(shuō)的是OC的方案,也就是使用runtime的方案剩檀,那么在swift下這個(gè)方案可不可行呢憋沿?
我實(shí)驗(yàn)了一下,Swift中的原生Array在property_getAttributes這個(gè)可以獲取泛型的API拿到的類型是NSArray沪猴,同時(shí)NSArray在這個(gè)API下拿到的也是NSArray辐啄,區(qū)分不開采章,好了,那我就拋棄runtime找找其他的解決方案吧壶辜。

首先找到的庫(kù)是Reflection悯舟,這個(gè)庫(kù)很吊很牛逼,牛逼到我現(xiàn)在還沒(méi)完全看懂它的代碼(當(dāng)然是我太菜了)砸民,里面大概的思路是拿到Swift下面的類的C指針抵怎,然后對(duì)指針去做偏移,從而動(dòng)態(tài)拿到這個(gè)類的屬性的一系列信息岭参,這個(gè)庫(kù)我看了下確實(shí)滿足了我所有需求反惕,但是秉持著作死的精神,我還是想自己找到一個(gè)看得懂的解決方案出來(lái)演侯。

第二個(gè)庫(kù)是GrandModel姿染,這個(gè)整體的方案和我之前的方案大致相似:通過(guò)Mirror動(dòng)態(tài)拿屬性列表,拿完之后用NSObject的setValue(_:forKey:)去做賦值秒际,那看來(lái)我的方案是可以實(shí)現(xiàn)我的需求的盔粹!那就這么做了!

由于加入了Swift的原生類型程癌,所以不能用Foundation框架里的API去做了,我想到的方案是Mirror的subjectType可以拿到當(dāng)前類的類型轴猎,那么我就需要遍歷當(dāng)前類的所有property嵌莉,對(duì)每個(gè)value取mirror,去拿類型再存起來(lái)捻脖。

  • 類型轉(zhuǎn)換問(wèn)題锐峭,數(shù)字類型轉(zhuǎn)String,直接走"\(value)"可婶,這個(gè)簡(jiǎn)單沿癞,string轉(zhuǎn)數(shù)字這個(gè)需要對(duì)每個(gè)數(shù)字類型進(jìn)行判斷,如果是Int要調(diào)Int的init矛渴,如果是Double要調(diào)double的init椎扬,這個(gè)還有優(yōu)化空間。
  • 泛型Array的處理具温,通過(guò)Mirror拿到的subjectType也是包含元素類型的信息的蚕涤,由于swift的所有Array和Dictionary必須要注明元素類型,所以我這里要做一個(gè)分支處理:如果Array的元素類型是Model铣猩,而且實(shí)際從服務(wù)端傳入的value也是[String:Any]的情況下才進(jìn)行Array的遍歷KVC揖铜,如果上面的條件不滿足,那么就要判斷類Array的元素類型和服務(wù)端value的元素類型是否一致达皿,如果不一致直接賦值會(huì)有crash風(fēng)險(xiǎn)天吓,核心代碼如下:
if valueType.hasPrefix("Array") {
    let arrayClassName = valueType.substring(with: valueType.index(valueType.startIndex, offsetBy: 6)..<valueType.index(valueType.endIndex, offsetBy: -1));
    if arrayClassName.hasPrefix("Dictionary<String") {
        if property.arrayClass != nil && property.arrayClass == .item {
            ret = type(of: self).itemsWithArray(value as! Array<Dictionary<String, Any>>);
        } else  {
            //arrayClass不是item贿肩,無(wú)需作數(shù)組泛型KVC,直接賦值龄寞,此處有因?yàn)镈ictionary的泛型不一致導(dǎo)致的crash風(fēng)險(xiǎn)汰规,待處理
            ret = value;
        }
    } else {
        ret = value;
    }
}
  • 父類的處理,我這里直接拋棄了Swift原生類型的繼承的處理萄焦,我覺(jué)得自己繼承Array去寫一個(gè)類的情況也比較少控轿,就簡(jiǎn)單處理了,沿用上面OC的處理就行了拂封,只是OC取父類是用runtime的API茬射,Swift里我用的是superMirror。
  • Foundation屬性的過(guò)濾冒签,這一條和OC處理方法一致在抛,在父類遞歸取屬性的時(shí)候,如果父類的類型以NS開頭萧恕,就直接break就行了刚梭。
  • 后續(xù)的工作

這次正好碰上離職,還有很多點(diǎn)沒(méi)做完票唆,比如Dictionary類型的強(qiáng)校驗(yàn)朴读,如果服務(wù)端給過(guò)來(lái)一個(gè)[String: String],本地是[String: Int]走趋,這種情況是有crash風(fēng)險(xiǎn)的衅金,本地要么做篩查,要么做轉(zhuǎn)換簿煌。

還有服務(wù)端字段名和本地屬性名不一致的話需要做映射氮唯。

還有取properties的時(shí)候的緩存。
還有是否需要throw出error姨伟,這個(gè)swift的新特性我還沒(méi)怎么用過(guò)呢惩琉。

框架地址:https://github.com/wsxiaoluob/LBMVC

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市夺荒,隨后出現(xiàn)的幾起案子瞒渠,更是在濱河造成了極大的恐慌,老刑警劉巖般堆,帶你破解...
    沈念sama閱讀 222,807評(píng)論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件在孝,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡淮摔,警方通過(guò)查閱死者的電腦和手機(jī)私沮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,284評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人仔燕,你說(shuō)我怎么就攤上這事造垛。” “怎么了晰搀?”我有些...
    開封第一講書人閱讀 169,589評(píng)論 0 363
  • 文/不壞的土叔 我叫張陵五辽,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我外恕,道長(zhǎng)杆逗,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,188評(píng)論 1 300
  • 正文 為了忘掉前任鳞疲,我火速辦了婚禮罪郊,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘尚洽。我一直安慰自己悔橄,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,185評(píng)論 6 398
  • 文/花漫 我一把揭開白布腺毫。 她就那樣靜靜地躺著癣疟,像睡著了一般。 火紅的嫁衣襯著肌膚如雪潮酒。 梳的紋絲不亂的頭發(fā)上睛挚,一...
    開封第一講書人閱讀 52,785評(píng)論 1 314
  • 那天,我揣著相機(jī)與錄音急黎,去河邊找鬼竞川。 笑死,一個(gè)胖子當(dāng)著我的面吹牛叁熔,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播床牧,決...
    沈念sama閱讀 41,220評(píng)論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼荣回,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了戈咳?” 一聲冷哼從身側(cè)響起心软,我...
    開封第一講書人閱讀 40,167評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎著蛙,沒(méi)想到半個(gè)月后删铃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,698評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡踏堡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,767評(píng)論 3 343
  • 正文 我和宋清朗相戀三年猎唁,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片顷蟆。...
    茶點(diǎn)故事閱讀 40,912評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡诫隅,死狀恐怖腐魂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情逐纬,我是刑警寧澤蛔屹,帶...
    沈念sama閱讀 36,572評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站豁生,受9級(jí)特大地震影響兔毒,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜甸箱,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,254評(píng)論 3 336
  • 文/蒙蒙 一育叁、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧摇肌,春花似錦擂红、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,746評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至肯适,卻和暖如春变秦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背框舔。 一陣腳步聲響...
    開封第一講書人閱讀 33,859評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工蹦玫, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人刘绣。 一個(gè)月前我還...
    沈念sama閱讀 49,359評(píng)論 3 379
  • 正文 我出身青樓樱溉,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親纬凤。 傳聞我的和親對(duì)象是個(gè)殘疾皇子福贞,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,922評(píng)論 2 361

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