Mac開發(fā)之一個Bug引發(fā)的三個思考

既然是一個Bug引發(fā)的思考,自然要先上Bug,如上動圖所示,在輸入了空格標題之后,引發(fā)一個問題,就是光標依然在文本框內,再敲擊鍵盤依然可以輸入改變文本框。


先彈窗后辭掉第一響應者.gif

gif不太好看,再來看下截圖


光標仍然存在.jpg
    本人本著不服輸?shù)木駥Υ藛栴}進行了深入研究,結果引發(fā)出來三個新的知識點,分別是:
    ①:光標還在文本框內,并不是NSALert窗口引發(fā)的Bug,但是NSAlert確實會打斷當前RunLoop循環(huán)內的事件傳遞響應鏈,同時還會影響本次循環(huán)內后續(xù)的部分UI更新功能,比如說,約束!。重要強調一下,是部分,不是所有!!!這個問題可以用下面的知識點③來解決,當然還有其它辦法,詳見
    ②:光標還在文本框內,原因是resignFirstResponder并不能真正辭掉NSTextField的第一響應者身份(這點跟iOS不同)。而且,當NStextField在編輯狀態(tài)中時,設置editable屬性為NO,能讓NSTextField變?yōu)椴豢删庉嫞仨毷切枰诋斍熬庉嬐瓿芍蟛趴梢裕乱淮蔚木庉嬑谋究騼热莶挪槐徽埱蟆<词惯@兩個方法同時被調用,也不能結束當前NSTextField的編輯狀態(tài),需要調用NSTextField的abortEditing方法來結束編輯狀態(tài)
     ③:當調用performSelectorOnMainThread: withObject: waitUntilDone:方法,當最后一個參數(shù)傳YES時,不僅僅會阻塞當前線程,而且會阻塞當前線程上的當前RunLoop循環(huán)。傳NO,則分兩種情況,一是在主線程上調用,會阻塞主線程,但不會阻塞當前RunLoop循環(huán);二是在子線程上調用,什么都不會阻塞。

===========================我是分割線===========================
枯燥的文字描述已經結束,下面就來講述我們的探究歷程:
先奉上這段問題代碼:
'''

  • (IBAction)editTitleBtnClick:(id)sender {
    self.titleLabel.editable = YES;
    [self.titleLabel becomeFirstResponder];

    __weak typeof(self) weakSelf = self;
    NSString *oldTitle = self.titleLabel.stringValue;

    self.mEventMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:NSLeftMouseDownMask | NSLeftMouseDraggedMask | NSLeftMouseUpMask handler:^NSEvent * _Nullable(NSEvent * _Nonnull event) {

      NSPoint p = [event locationInWindow];
      NSPoint newP = [weakSelf.titleLabel convertPoint:p fromView:nil];
      
    
      //當點擊區(qū)域在TextField外時
      if (!CGRectContainsPoint(weakSelf.titleLabel.bounds, newP)) {
    
          [NSEvent removeMonitor:self.mEventMonitor];
          weakSelf.mEventMonitor = nil;
          NSString *newTitle = [weakSelf.titleLabel.stringValue stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
          
          
          if (newTitle.length <= 0){
              weakSelf.titleLabel.stringValue = oldTitle;
              [YHNAlertWindow alertToShowMessageWithButtonTitle:@"確定" AndMessageText:@"請輸入分組名稱"];
    

// [weakSelf performSelectorOnMainThread:@selector(showMessage) withObject:nil waitUntilDone:NO]; //標記1
}else if(![oldTitle isEqualToString:newTitle]){
[weakSelf editFinishedBtnClick];
}

        [weakSelf.titleLabel resignFirstResponder];
        weakSelf.titleLabel.editable = NO;
        //回復文本框為Label
        return event;//標記2
    }
    return event;
}];

}

  • (void)showMessage{
    [YHNAlertWindow alertToShowMessageWithButtonTitle:@"確定" AndMessageText:@"請輸入分組名稱"];//標記3
    }
    '''
    一、關于NSTextField辭去第一響應者的問題
    首先一開始,確實以為NSTextField不能正常辭掉第一響應者身份是NSAlert引發(fā)的,所以把彈框和辭掉響應者的代碼位置調整了一下,然后變成先辭掉第一響應者,再彈窗,下面請看問題Gif


    先辭掉第一響應者后彈窗.gif

再來看下截圖


先辭掉第一響應者后彈窗2.jpg

我去,這下問題更大了,不僅光標沒去掉,還可以接受文本編輯事件,NSTextField更是沒有自適應大小!你妹啊。
沒辦法,自好再去考慮其他的問題,想來想去,可能是因為我這段代碼是在錨點事件回調內部產生的,所以可能會受到點影響,于是就去外面寫了一個Demo(代碼特別簡單,就不再奉上了)測試下,結果意外發(fā)現(xiàn),在沒有NSAlert彈窗和錨點事件回調的影響的情況下,NSTextField依然沒有辭掉第一響應者身份,光標也依然在,文本也在改變。蒼天啊,大地啊,resignFirstResponder不好使了嘛?我以前在iOS里都是這么調的啊。沒辦法,只好去翻NSTextField的頭文件,不出意外,在仔仔細細看了一整遍NSTextField.h文件后,依然一無所獲,在此有種想哭的感覺。然后看NSTextField繼承,結果在NSControl.h中發(fā)現(xiàn)了一個 abortEditing 方法。如或至寶啊,那就拿過來試試,恩,果然好使。
好了,問題解決了,然后經過把下面三個方法各種組合,在NSTextField各種狀態(tài)下縝密測試,得出下面結論:
1、resignFirstResponder:確實沒有使NSTextField辭掉第一響應者身份
2、setEditable:設置editable屬性為NO,也確實能讓NSTextField變?yōu)椴豢删庉嫛5O置是,必須確保NSTextField不再編輯狀態(tài)下,否則當前還是可以繼續(xù)編輯文本框內容的,下一次的編輯文本框內容才不被接受。
3、abortEditing:強制NSTextField退出文本編輯狀態(tài),但是有一個Bug,那就是拼音輸入漢字的時候,如果沒有點空格就調用此方法,會把一堆字母加單引號輸進去,目前除了去controlTextDidEndEditing里做攔截,還真沒想到其它好的方法。

    在剛總結完上面結論的時候,發(fā)現(xiàn)了第二個問題,那就是我的點擊事件被攔截了!!!來,先看看Gif,確定下發(fā)生了什么事情:
阻擋事件傳遞.gif

當我在點擊好友,以此來觸發(fā)彈窗的時候,我點擊好友竟然沒效果!!!有彈窗,有錨點事件,斷點調試不太友好,所以只好苦逼的去打Log,結果發(fā)現(xiàn)event被return出去了,也就是說我的代碼正常被執(zhí)行了,卻沒出來該有的效果。
涉及到這方面的問題,我第一個想到的就是RunLoop,姑且算是死馬當活馬醫(yī)吧,于是也用了一個死馬當活馬醫(yī)的嘗試,就是用performSelectorOnMainThread:withObject:waitUntilDone:函數(shù)回調到主線程來彈窗。在主線程里回調主線程去拋彈窗,腦子有問題吧,O__O "…就當是吧,反正試一試又不會懷孕,萬一要是解決問題了呢?
恩,還真就解決問題了呢!來,再看gif:


完美.gif

好了,問題是解決了,我們來分析下,為什么回調主線程就能解決問題呢?
二、關于模態(tài)窗口打斷事件傳遞響應鏈的問題分析
首先,我們找到蘋果官方文檔中對于NSAlert的描述,有這么一段話
'''
An alert appears onscreen either as an app-modal dialog or as a sheet attached to a document window. The methods of the NSAlert class allow you to specify alert level, alert text, button titles, and a custom icon should you require it. The class also lets your alerts display a help button and provides ways for apps to offer help specific to an alert.
'''
很長的描述,但是對我們有用的只有第一句話,就是An alert appears onscreen either as an app-modal dialog or as a sheet attached to a document window.這句,用我的理解翻譯過來就是,NSAlert彈出的是一個模態(tài)窗口。但是這種窗口老霸道了,當它啟動以后,僅它自己可以接收和相應用戶操作,無法切換到其他窗口操作,其他窗口也不能接收處理系統(tǒng)內部各種事件。同時,如果在你彈出模態(tài)窗口的的RunLoop循環(huán)內,當前的系統(tǒng)事件沒有傳遞完的話,也會被打斷,無法繼續(xù)傳遞下去,但是,關于這,貌似有一個小Bug,請看Gif。

    好了,既然說明了問題所在,那我們就來說明為什么這么做就能解決問題:
    performSelectorOnMainThread:withObject:waitUntilDone:當在在主線程中調用此函數(shù)的時候,①如過末尾參數(shù)傳遞YES;則不僅僅會阻塞線程,還會阻塞當前RunLoop循環(huán)。這樣就相當于把回調的selector方法里的代碼放到當前位置執(zhí)行,這樣就只有一個結果,那就是沒任何效果。換成白話講就是,在代碼中標記1到標記3的執(zhí)行順序是:標記1→標記3→標記2,而且這三處代碼在同一個RunLoop循環(huán)內執(zhí)行。
   ②如果末尾參數(shù)傳NO,則會產生另外一種代碼執(zhí)行順序:標記1→標記2→標記3。客官,請注意,當前改變的不僅僅是代碼的執(zhí)行順序,還有另外一層更重要的改變,就是在執(zhí)行完標記2代碼后,并不是立刻執(zhí)行標記3的代碼,而是等到下一次RunLoop循環(huán)的時候才會由系統(tǒng)調用執(zhí)行標記3的代碼。說到這里就已經很明白了,由于彈出Alert窗口的RunLoop循環(huán)已經不再是原來的循環(huán),等及彈窗的時候原來的循環(huán)早已執(zhí)行完畢,用戶的點擊事件也早已在那個循環(huán)內被傳遞并且響應完畢,故就能解決上述問題。
   ③上述問題還有多種解決方案,那就是多線程,具體做法就是在主線程內開啟異步回調主線程進行彈框,至于原理,跟上述一樣,都是利用代碼的執(zhí)行時間差來解決問題,這或許是另外的一種“異步”吧。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,117評論 6 537
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,860評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,128評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,291評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,025評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,421評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,477評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,642評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 49,177評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,970評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,157評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,717評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,410評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,821評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,053評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,896評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,157評論 2 375

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,682評論 25 708
  • *7月8日上午 N:Block :跟一個函數(shù)塊差不多,會對里面所有的內容的引用計數(shù)+1,想要解決就用__block...
    炙冰閱讀 2,512評論 1 14
  • 2017.02.22 可以練習,每當這個時候,腦袋就犯困,我這腦袋真是神奇呀,一說讓你做事情,你就犯困,你可不要太...
    Carden閱讀 1,363評論 0 1
  • 原創(chuàng)洋醬 在日本有種說法,稱2歲左右的孩子為“魔の二歳”,意思是說,兩歲的孩子就像個謎團一樣無法理解,會做出各種各...
    洋醬閱讀 1,094評論 0 3
  • ——安迪 安德魯斯《上得天堂,下得地獄》 勇氣,亦或叫做激情與灑脫更準確。但我更喜歡勇氣這個詞。激情的背后是瘋狂和...
    寧謐隨風閱讀 191評論 0 0