iOS-NSUndoManager與怎樣弄崩微信

檢查項目bug的時候偶然發(fā)現(xiàn)察蹲,做過限制(比如說字數(shù)锰瘸、表情)的textField粹淋、textView,觸發(fā)限制條件后监透,會在使用undo功能時crash,之后發(fā)現(xiàn)微信也是一樣的蛋辈。
有朋友問在哪里崩了属拾,不能復現(xiàn),我舉幾個例子,其實有字數(shù)限制的輸入框應該都有問題

我->個人信息->我的地址->新增地址
我->個人信息->名字
我->個人信息->個性簽名

隨便試了試qq渐白、yy尊浓、簡書、喜馬拉雅的能輸入漢字的輸入框的字數(shù)限制纯衍,發(fā)現(xiàn)qq一般只提示不限制栋齿;yy禁用了undo;簡書沒做限制托酸;做的最爛的是喜馬拉雅褒颈,做了限制,但是可以輕松突破励堡,輸入任意長度的字符串谷丸。

DEMO

https://github.com/liulishuo/testUndo

思路

出現(xiàn)crash是因為,為了實現(xiàn)輸入的過濾效果应结,會監(jiān)聽輸入框的UIControlEventEditingChanged事件刨疼,截取字符串,手動給輸入框的text屬性賦值鹅龄。正常情況下輸入框執(zhí)行setText:揩慕,默認不會注冊到自己的undoManager上,并且會清空undoManger的undo扮休、redo棧迎卤,這樣并沒有問題,問題是在于監(jiān)聽UIControlEventEditingChanged事件所執(zhí)行的方法里是先對輸入框的text做截取然后執(zhí)行setText:玷坠。
看起來是截取的操作會入undo棧蜗搔,之后的setText:方法并不會清空undo棧,導致做undo操作時八堡,逆操作的是字符串截取的操作樟凄,操作的數(shù)據(jù)對不上,導致崩潰兄渺,這是我覺得比較合理的解釋缝龄。

*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[NSBigMutableString substringWithRange:]: Range {6, 4} out of bounds; string length 6'

以此為前提,我們有三個解決問題的方向:

  • 禁用undo功能挂谍,繞過去叔壤,yy是這樣做的。

  • 使用setText:凳兵,每次在過濾操作時先將setText:注冊到undoManager上百新,再進行setText:賦值操作填帽。我試過不行徒欣。
    還是一樣的錯誤-[NSBigMutableString substringWithRange:] range超限了缺亮,setText:的逆操作為啥也是這個澈侠,我不清楚。

  • 使用setText:铅辞,并確保和系統(tǒng)默認行為一致厌漂,也就是用setText:賦值,并清空undo棧斟珊。
    個人覺得這樣能達到目的苇倡,最方便。

實現(xiàn)

先說微信囤踩,微信的輸入框特點是:
1.漢字聯(lián)想的時的字符數(shù)也一樣有限制
2.文本長度滿了旨椒,輸入框就不能從任意位置插入任何字符(可能是為了規(guī)避系統(tǒng)九宮格鍵盤輸入漢字的問題)
(不太好歸納,我的地址的收貨人輸入框貌似有兩套邏輯堵漱,一個是最大長度16個字综慎,另外一個是最大長度50個字,我每次crash回來都會切換勤庐。示惊。。愉镰,但是兩套邏輯都有各自的問題米罚,有興趣的同學自己試一下,我們這里只討論會crash的情況丈探,也就是最大長度16個字的限制條件下的問題)
所以我一開始以為录择,微信應該是這么實現(xiàn)的

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
     //退格
    if([string isEqualToString:@""])
    {
        return YES;
    }
    
    //文本長度滿不允許編輯 防止系統(tǒng)九宮格鍵盤在此時傳入數(shù)字標號字符
    if(textField.text.length >= kMaxLength)
    {
        return NO;
    }
    
    //非聯(lián)想狀態(tài)
    if(!textField.markedTextRange)
    {
        NSString * tempString = [textField.text stringByReplacingCharactersInRange:range withString:string];
        NSLog(@"%@",tempString);
        
        if (tempString.length > kMaxLength)
        {
            textField.text = [tempString substringToIndex:kMaxLength];
            return NO;
        }
    }
    
    return YES;
}

但是這樣寫不會因為undo而crash,并且還有漢字聯(lián)想無限輸入的bug碗降。
所以微信應該還用了這種方式

//微信

[_tf addTarget:self action:@selector(textFieldTextDidChanged:) forControlEvents:UIControlEventEditingChanged];

- (void)textFieldTextDidChanged:(UITextField *)sender
{
    NSString * tempString = sender.text;

    if (sender.markedTextRange == nil && tempString.length > kMaxLength)
    {
        sender.text = [tempString substringToIndex:kMaxLength];
    }
}

這種方式除了undo會crash糊肠,沒有其他明顯的漏洞。

修復這個bug遗锣,只需要加一行代碼

- (void)textFieldTextDidChanged:(UITextField *)sender
{
    NSString * tempString = sender.text;
    
    if (sender.markedTextRange == nil && tempString.length > kMaxLength)
    {
        sender.text = [tempString substringToIndex:kMaxLength];
        [sender.undoManager removeAllActions];
    }
}

just for fun 我們來猜一下其他人的實現(xiàn)
yy的實現(xiàn)(機智)

//yy
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
     application.applicationSupportsShakeToEdit = NO;
    
    return YES;
}

喜馬拉雅的實現(xiàn)(漏洞最多)

//喜馬拉雅
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
    if(range.location >= kMaxLength)
    {
        return NO;
    }
    else
    {
        return YES;
    }
}

其實我覺得在用戶的輸入階段就屏蔽掉某些可能的輸入,真是一件吃力不討好的事情嗤形。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末精偿,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子赋兵,更是在濱河造成了極大的恐慌笔咽,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件霹期,死亡現(xiàn)場離奇詭異叶组,居然都是意外死亡,警方通過查閱死者的電腦和手機历造,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門甩十,熙熙樓的掌柜王于貴愁眉苦臉地迎上來船庇,“玉大人,你說我怎么就攤上這事侣监⊙悸郑” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵橄霉,是天一觀的道長窃爷。 經(jīng)常有香客問我,道長姓蜂,這世上最難降的妖魔是什么按厘? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮钱慢,結果婚禮上逮京,老公的妹妹穿的比我還像新娘。我一直安慰自己滩字,他們只是感情好造虏,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著麦箍,像睡著了一般漓藕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上挟裂,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天享钞,我揣著相機與錄音,去河邊找鬼诀蓉。 笑死栗竖,一個胖子當著我的面吹牛,可吹牛的內容都是我干的渠啤。 我是一名探鬼主播狐肢,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼沥曹!你這毒婦竟也來了份名?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤妓美,失蹤者是張志新(化名)和其女友劉穎僵腺,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體壶栋,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡辰如,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了贵试。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片琉兜。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡凯正,死狀恐怖,靈堂內的尸體忽然破棺而出呕童,到底是詐尸還是另有隱情漆际,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布夺饲,位于F島的核電站奸汇,受9級特大地震影響,放射性物質發(fā)生泄漏往声。R本人自食惡果不足惜擂找,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望浩销。 院中可真熱鬧贯涎,春花似錦、人聲如沸慢洋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽普筹。三九已至败明,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間太防,已是汗流浹背妻顶。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蜒车,地道東北人讳嘱。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像酿愧,于是被迫代替她去往敵國和親沥潭。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內容