項目常見崩潰4(陸續(xù)更新)

前情提要, 上回書說UIAlertView會在iOS9以下的系統(tǒng)上產(chǎn)生內(nèi)存泄露, 解決方案是要么退出VC的時候直接關(guān)閉alertView要么全局控制alertView. 上回在講到關(guān)閉alertView的時候談到了崩潰, 今天重點說下UIAlertView退出時候的崩潰.

崩潰重現(xiàn)

顯示alertView
    UIAlertView* alertView = [[UIAlertView alloc] initWithTitle:@""
                                                        message:@"您的號在另一終端登錄"
                                                       delegate:self
                                              cancelButtonTitle:@"確定"
                                              otherButtonTitles:@"取消", nil];
    [alertView show];
    self.alertView = alertView;
    [self.navigationController popViewControllerAnimated:NO];

這里顯示之后用一個強指針指著, 防止直接釋放, 然后立即pop掉當(dāng)前controller, 并在dealloc中執(zhí)行如下方法

- (void)dealloc {
    [self.alertView dismissWithClickedButtonIndex:0 animated:YES];
}

好么, 這不就是上回書說的方案1嗎?怎么還會崩潰!
先看下崩潰日志:

2017-01-18 09:28:59.667 28-UIAlertViewCrash[1023:16310] Trying to dismiss the presentation controller while transitioning already. (<_UIAlertControllerAlertPresentationController: 0x7874eab0>)
2017-01-18 09:28:59.668 28-UIAlertViewCrash[1023:16310] transitionViewForCurrentTransition is not set, presentation controller was dismissed during the presentation? (<_UIAlertControllerAlertPresentationController: 0x7874eab0>)

WTF, 你試圖dismiss一個已經(jīng)在過渡的VC了, 這個VC是_UIAlertControllerAlertPresentationController, 貌似有點眼熟, _UIAlertControllerShimPresenterWindow上回提到UIAlertView被添加的window就是這個window, 那看這架勢_UIAlertControllerAlertPresentationController必然是這個window內(nèi)的一個controller了, 為了驗證我們的說法, 打印下面的日志

    UIWindow *keyWindow = [UIApplication sharedApplication].keyWindow;
    NSLog(@"keywindow:%p:%@", keyWindow, NSStringFromClass([keyWindow class]));
    
    UIViewController *viewController = keyWindow.rootViewController;
    NSLog(@"keywindow's root vc:%p:%@", viewController, NSStringFromClass([viewController class]));

結(jié)果是:

2017-01-18 09:38:29.478 28-UIAlertViewCrash[1086:21182] keywindow:0x7b94a900:_UIAlertControllerShimPresenterWindow
2017-01-18 09:38:29.478 28-UIAlertViewCrash[1086:21182] keywindow's root vc:0x7b839ba0:_UIAlertShimPresentingViewController

沒錯了, _UIAlertControllerAlertPresentationController就是_UIAlertControllerShimPresenterWindow的rootViewController. 繼續(xù)說崩潰, 崩潰發(fā)生在iOS8.X系統(tǒng)上, 來看看iOS9以上運行的結(jié)果

結(jié)果是不會崩潰, 而且是在VC被干掉的時候也關(guān)閉了UIAlertView, 這不正好是我們想要的結(jié)果嗎, 實際上iOS9以上已經(jīng)做到了, 而且我們的代碼一點都沒變啊! 良心企業(yè)有木有, 但是我們還是要兼容iOS8, 因為iOS現(xiàn)在出到10, 一般的公司都會兼容3個版本, 所以iOS8上的崩潰那也是崩潰, 還是要處理的.

矛盾

對于iOS8.X系統(tǒng)如果不在dealloc的時候dismiss會有內(nèi)存泄露, 如果dismiss就可能會崩潰, 這可如何是好啊! 從崩潰日志上看, alertView在顯示的時候是創(chuàng)建了一個VC的, 然后這個VC是通過pressent的方式添加到當(dāng)前keywindow上的, 那么我們嘗試hook presentViewController

代碼如下

NSString* const finishPresenting = @"finishPresenting";

@implementation UIViewController (hook)

+ (void)load {
    Class class = [self class];
    SEL oriSel = @selector(presentViewController:animated:completion:);
    SEL newSel = @selector(myPresentViewController:animated:completion:);
    Method fromMethod = class_getInstanceMethod(class, oriSel);
    Method toMethod = class_getInstanceMethod(class, newSel);
    if (!class_addMethod(class, oriSel, method_getImplementation(toMethod), method_getTypeEncoding(toMethod))) {
        method_exchangeImplementations(fromMethod, toMethod);
    }
}
- (void)myPresentViewController:(UIViewController *)viewControllerToPresent animated: (BOOL)flag completion:(void (^ __nullable)(void))completion {
    self.presenting = YES;
    NSLog(@"myPresentViewController called:%p: %@", viewControllerToPresent, NSStringFromClass([viewControllerToPresent class]));
    __weak typeof(self) weak_self = self;
    [self myPresentViewController:viewControllerToPresent animated:flag completion:^{
        weak_self.presenting = NO;
        if (completion) {
            completion();
        }
        [[NSNotificationCenter defaultCenter] postNotificationName:finishPresenting object:nil];
    }];
}

- (void)setPresenting:(BOOL)presenting {
    objc_setAssociatedObject(self, @selector(isPresenting), @(presenting), OBJC_ASSOCIATION_ASSIGN);
}

- (BOOL)isPresenting {
    return [objc_getAssociatedObject(self, _cmd) boolValue];
}
@end

這樣在present開始的時候去設(shè)置present的狀態(tài), 結(jié)束后重置狀態(tài)并拋通知, 這個通知在你的自定義導(dǎo)航控制器中監(jiān)聽

- (UIViewController *)popViewControllerAnimated:(BOOL)animated {
    NSLog(@"popViewControllerAnimated:animated:%d", animated);
    if ([UIApplication sharedApplication].keyWindow.rootViewController.isPresenting) {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onFinishPresenting:) name:finishPresenting object:nil];
        });
        return nil;
    }
    return [super popViewControllerAnimated:animated];
}

- (void)onFinishPresenting:(NSNotification *)notification {
    [self popViewControllerAnimated:NO];
}

這樣當(dāng)你進行pop操作的時候先看下是否有正在presenting, 如果有就監(jiān)聽這個結(jié)束通知, 等結(jié)束后再繼續(xù)執(zhí)行, 這里只是簡單的代碼, 實際pop的時候還要進行其它的判斷, 用隊列比較合適, 這里為了簡單說明問題, 只是簡單處理下, 方便讀者閱讀.

好了, 到這里應(yīng)該就沒崩潰了, 我們再重新驗證下下面幾個問題
1 還有內(nèi)存泄露嗎?
2 還崩潰嗎?
3 在其它系統(tǒng)上有沒有問題?

答案是:
1 還有內(nèi)存泄露(這里真的不知道為什么會這樣, 只能如實稟告大家)
2 沒有崩潰了
3 在其它機器上是沒有問題的, 沒內(nèi)存泄露也沒有崩潰, 異常日志都沒有.

這里還是有一個關(guān)于邏輯的問題, 如果有彈窗, 那就不允許pop還是等彈窗出來再pop, 實際上, 大部分情況是, 彈窗都是會指定頁面的, 只有被強制退出這種彈窗才會伴隨pop, 而這種彈窗的數(shù)量應(yīng)該是很少的, 應(yīng)該是做成全局的. 對于頁面內(nèi)的彈窗, 個人感覺如果此時恰好要pop是可以選擇不進行pop的, 你可以繼續(xù)操作彈窗.

總結(jié)

對一個廢棄的東西UIAlertView說了這么多, 總結(jié)一下這里涉及到很多我們平時不太關(guān)注的東西, 作為一個開發(fā), 應(yīng)該是要了解更多的, 不能只是會用.
1 AppDelegate的window和keyWindow不一樣, keyWindow是當(dāng)前正在顯示的window, 而AppDelegate里的window是我們應(yīng)用真切能夠用到的window
2 UIAlertView實際通過present的方式添加到keyWindow上, 在present的時候進行去執(zhí)行dismiss是會崩潰的, 這點即使使用UIAlertController也是需要注意的.
3 在有present進行的時候盡量不要進行push或者pop造作, 因為這樣很容易導(dǎo)致一些UI的釋放操作, 比如dismiss, 進而可能會崩潰.
4 許多詭異的崩潰可能往往就是我們不懂其中的原理導(dǎo)致的, 如果大家都知道UIAlertView是通過present方式添加并且present不允許dismiss我想就不會有這種崩潰了, 但是, 顯然大部分開發(fā)者都是不清楚的.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末残邀,一起剝皮案震驚了整個濱河市枣耀,隨后出現(xiàn)的幾起案子胚股,更是在濱河造成了極大的恐慌,老刑警劉巖士八,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異梁呈,居然都是意外死亡婚度,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蝗茁,“玉大人醋虏,你說我怎么就攤上這事∠蹋” “怎么了颈嚼?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長饭寺。 經(jīng)常有香客問我阻课,道長,這世上最難降的妖魔是什么艰匙? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任限煞,我火速辦了婚禮,結(jié)果婚禮上员凝,老公的妹妹穿的比我還像新娘署驻。我一直安慰自己,他們只是感情好健霹,可當(dāng)我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布旺上。 她就那樣靜靜地躺著,像睡著了一般糖埋。 火紅的嫁衣襯著肌膚如雪宣吱。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天阶捆,我揣著相機與錄音凌节,去河邊找鬼。 笑死洒试,一個胖子當(dāng)著我的面吹牛倍奢,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播垒棋,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼卒煞,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了叼架?” 一聲冷哼從身側(cè)響起畔裕,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎乖订,沒想到半個月后扮饶,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡乍构,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年甜无,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡岂丘,死狀恐怖陵究,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情奥帘,我是刑警寧澤铜邮,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站寨蹋,受9級特大地震影響松蒜,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜钥庇,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一牍鞠、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧评姨,春花似錦难述、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至嗦枢,卻和暖如春攀芯,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背文虏。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工侣诺, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人氧秘。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓年鸳,卻偏偏與公主長得像,于是被迫代替她去往敵國和親丸相。 傳聞我的和親對象是個殘疾皇子搔确,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,802評論 2 345

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

  • 上回書說animated隱式動畫的血與淚, 今天要說的可能和崩潰無關(guān), 但是又是很多詭異的崩潰的根源: 內(nèi)存泄露....
    bigParis閱讀 864評論 0 2
  • UIWindow是Cocoa框架的重要組件之一,所有的UIView都要通過UIWindow來進行展現(xiàn)灭忠,沒有UIWi...
    文興閱讀 4,924評論 3 34
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫膳算、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,024評論 4 62
  • 從去年下半年幫哥們頂了幾個月也算干了小半年了弛作,在口碑時代公關(guān)先行的趨勢也越來越明顯了涕蜂。而公關(guān)有眾多分支但最有挑戰(zhàn)的...
    賤雞行事閱讀 412評論 0 0
  • “狗子,你是去吃飯嗎映琳?給我?guī)€晚飯吧宇葱!” “大黃瘦真,我手機停機了,幫我交下話費黍瞧,我給你現(xiàn)金≡樱” …… 我得承認(rèn)這是我...
    波浪卷兒閱讀 1,025評論 2 17