淺談iOS13中使用KVC設(shè)置UITextField占位符崩潰的原因

眾所周知,在iOS13中使用KVC設(shè)置UITextField占位符會(huì)導(dǎo)致崩潰,出于好奇,今天我對(duì)崩潰原因進(jìn)行了一番簡(jiǎn)單探索,現(xiàn)將探索過(guò)程記錄如下:

[textfield setValue:[UIColor redColor] forKeyPath:@"_placeholderLabel.textColor"];

上面這段代碼在iOS13之前一直工作良好,更新到iOS13后莫名其妙就崩潰了,需要通過(guò)設(shè)置attributedPlaceholder屬性來(lái)代替。很多人覺(jué)得是iOS13對(duì)KVC的支持變?nèi)趿?,我覺(jué)得不太可能,應(yīng)該是蘋(píng)果爸爸偷偷把私有實(shí)例變量名(或?qū)傩悦┙o改了。廢話不多說(shuō),開(kāi)始驗(yàn)證,首先下載一個(gè)Xcode11,分別在iOS13和iOS12系統(tǒng)中運(yùn)行以下代碼,通過(guò)runtime獲取并打印UITextField的所有屬性:

unsigned int count;
objc_property_t *ptr = class_copyPropertyList([textfield class], &count);
for (NSUInteger i = 0; i < count; i++) {
    NSLog(@"%s", property_getName(ptr[i]));
}
free(ptr);

運(yùn)行結(jié)果顯示,無(wú)論是在iOS12還是iOS13環(huán)境下,都沒(méi)有“placeholderLabel”相關(guān)的屬性,所以_placeholderLabel不是UITextField的屬性。既然能用KVC訪問(wèn),不是屬性,就應(yīng)該是實(shí)例變量咯,繼續(xù)驗(yàn)證,我們通過(guò)runtime獲取并打印UITextField的所有實(shí)例變量:

unsigned int count2;
Ivar *ptr2 = class_copyIvarList([textfield class], &count2);
for (NSUInteger i = 0; i < count2; i++) {
    NSLog(@"%s", ivar_getName(ptr2[i]));
}
free(ptr2);

果然,我們?cè)谌f(wàn)花叢中如愿找到了_placeholderLabel,遺憾的是,無(wú)論在iOS12還是iOS13環(huán)境下,UITextField都存在這個(gè)實(shí)例變量。顯然,我一開(kāi)始的推測(cè)被啪啪打臉了。

image.png

一條路走不通,馬上調(diào)整思路,去找其他線索,不防先來(lái)仔細(xì)研究一下蘋(píng)果給的崩潰信息

image.png

結(jié)合崩潰堆棧和拋出的異常信息繼續(xù)分析,首先,崩潰是發(fā)生在-[UITextField valueForKey:],也就是說(shuō),UITextField獲取_placeholderLabel時(shí)其實(shí)就已經(jīng)崩潰了;再來(lái)看異常信息,“access to UITextField's _placeholderLabel ivar is prohibited. This is an application bug”,_placeholderLabel被禁止使用,這是一個(gè)應(yīng)用程序bug,不知道大家之前有沒(méi)有遇到這種異常,我是從來(lái)沒(méi)遇到過(guò),通常都是valueForUndefinedKey:異常。
接下來(lái)我們驗(yàn)證一下蘋(píng)果是否修改了valueForKey:的邏輯,我們找一個(gè)和_placeholderLabel同級(jí)別的實(shí)例變量_textContentView,同樣按照按照KVC方式去設(shè)置其背景色

[textfield setValue:[UIColor redColor] forKeyPath:@"_textContentView.backgroundColor"];

在iOS13環(huán)境下運(yùn)行代碼,沒(méi)有崩潰,而且textfield背景色被修改成了紅色,說(shuō)明這段代碼已經(jīng)生效了。至此,valueForKey:也得以沉冤得雪,退出了背鍋群!
還有一種可能,蘋(píng)果在通過(guò)valueForKey:獲取實(shí)例時(shí),對(duì)值為“_placeholderLabel”的key作了特殊處理,當(dāng)發(fā)現(xiàn)key為“_placeholderLabel”時(shí),拋出上文中提到的異常。怎么證明呢,我們知道KVC獲取實(shí)例時(shí),對(duì)下劃線并不敏感,我們可以去掉下劃線來(lái)試試。

[textfield setValue:[UIColor redColor] forKeyPath:@"placeholderLabel.textColor"];

iOS13環(huán)境下運(yùn)行?。?!巧了不是!巧了不是!可以正常工作了。忙活半天,總算找到原因了。天下武功出少林,天下iOS奇淫技巧出runtime,接下來(lái),又輪到萬(wàn)能的runtime上場(chǎng)啦,添加以下代碼,老代碼不用作任何修改,又可以天下太平了:

@implementation UITextField (KVC)

+ (void)load {
    Method origin = class_getInstanceMethod([self class], @selector(valueForKey:));
    Method swizzing = class_getInstanceMethod([self class], @selector(swizzing_valueForKey:));
    if (class_addMethod([self class], @selector(valueForKey:), method_getImplementation(swizzing), method_getTypeEncoding(swizzing))) {
        class_replaceMethod([self class], @selector(swizzing_valueForKey:), method_getImplementation(origin), method_getTypeEncoding(origin));
    }
    method_exchangeImplementations(origin, swizzing);
}

- (id)swizzing_valueForKey:(NSString *)key {
    if ([key isEqualToString:@"_placeholderLabel"]) {
        Ivar ivar = class_getInstanceVariable([self class], [key UTF8String]);
        id value = object_getIvar(self, ivar);

        return value;
    } else {
        return [self swizzing_valueForKey:key];
    }
}

當(dāng)然,這段代碼主要是用來(lái)驗(yàn)證猜想,不建議在開(kāi)發(fā)中使用。蘋(píng)果的意圖很明顯,就是為了讓我們使用公開(kāi)的attributedPlaceholder屬性來(lái)代替私有API。

后記

這篇文章并沒(méi)有多少開(kāi)發(fā)干貨,主要是為了記錄一下自己探索的過(guò)程,希望能對(duì)今后定位同類(lèi)型問(wèn)題時(shí)提供一種思路。另外,文中如果有任何紕漏或錯(cuò)誤,歡迎大家批評(píng)指正!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前提 這段時(shí)間升級(jí)了 Xcode11.0,在 iOS13.0 運(yùn)行的時(shí)候,當(dāng)運(yùn)行到 [textField setV...
    kwdx閱讀 4,246評(píng)論 5 20
  • KVC(Key-value coding)鍵值編碼,單看這個(gè)名字可能不太好理解。其實(shí)翻譯一下就很簡(jiǎn)單了,就是指iO...
    我的夢(mèng)工廠閱讀 939評(píng)論 1 8
  • KVC(Key-valuecoding)鍵值編碼,單看這個(gè)名字可能不太好理解。其實(shí)翻譯一下就很簡(jiǎn)單了,就是指iOS...
    榕樹(shù)頭閱讀 772評(píng)論 0 2
  • 占位文字1、曾經(jīng)有個(gè)這么一個(gè)項(xiàng)目需求: 使用textField時(shí),占位文字默認(rèn)是黑色的,我們的需求是當(dāng)開(kāi)始編輯時(shí),...
    博行天下閱讀 1,024評(píng)論 2 1
  • KVC(Key-value coding)鍵值編碼,iOS的開(kāi)發(fā)中,可以允許開(kāi)發(fā)者通過(guò)Key名直接訪問(wèn)對(duì)象的屬性,...
    CALayer_Sai閱讀 2,699評(píng)論 0 4

友情鏈接更多精彩內(nèi)容