關(guān)于項(xiàng)目中崩潰問題處理:Can't add self as subview

項(xiàng)目中bugly總是收集到Can't add self as subview 的崩潰
錯(cuò)誤,崩潰調(diào)用堆棧解析如下:

圖:
3D520C11-0552-41A6-8FB1-442E0AEC48C1.png

通過分析崩潰堆棧日志解析和崩潰信息提示,只能得出兩個(gè)線索:
1.addSubView的參數(shù)是自己本身self;
2.崩潰和navigationController轉(zhuǎn)場(chǎng)動(dòng)畫有關(guān)。

排查

1.addSubView的參數(shù)是自己本身self

在工程中用 [self addSubView:self] 測(cè)試,確實(shí)會(huì)崩潰,崩潰的堆棧信息如下。和上傳的崩潰信息不一致,可以排除。


屏幕快照 2019-02-12 18.05.08.png

2.navigationController轉(zhuǎn)場(chǎng)動(dòng)畫

navigationController push或者pop上一次操作還沒有完成就開始執(zhí)行下一次操作,同步執(zhí)行了多個(gè)轉(zhuǎn)場(chǎng)操作。如下代碼:

    [self.navigationController pushViewController:[[SecondAViewController alloc] init] animated:NO];
    [self.navigationController pushViewController:[[SecondBViewController alloc] init] animated:YES];
    [self.navigationController pushViewController:[[SecondCViewController alloc] init] animated:YES];

同時(shí)執(zhí)行完以上操作,之后的pop退場(chǎng)操作就會(huì)導(dǎo)致崩潰。崩潰信息如下:


屏幕快照 2019-02-13 16.37.35.png

push SecondCViewController,C成功入棧,但是視圖沒有加載到容器中,實(shí)際顯示的還是B的vc與view,但是棧頂是C的vc。

第一次點(diǎn)擊返回時(shí),實(shí)際是C出棧,但是當(dāng)前顯示的視圖B的view被先加載到formAnimateView與toAnimateView上,原本視圖在出棧后應(yīng)該被釋放,但是現(xiàn)在容器棧內(nèi)還存在B的vc;
當(dāng)?shù)诙吸c(diǎn)擊返回時(shí),實(shí)際應(yīng)該是B的vc出棧,但是A的view加載到toAnimateView上之后,toAnimateView需要加載到wrapperView進(jìn)行transition動(dòng)畫, 但wrapperView通過棧頂元素view.superview取值, 而棧頂元素B的view由于上一次錯(cuò)誤的轉(zhuǎn)場(chǎng), 并未在transition動(dòng)畫完成后掛載到wrapperView, 還保留在的臨時(shí)的動(dòng)畫視圖toAnimateView上, 所以使toAnimateView加載到WrapperView的操作變成了動(dòng)畫視圖toAnimateView加載到自己上

解決方法

導(dǎo)致轉(zhuǎn)場(chǎng)異常的根本原因是上一次操作還沒執(zhí)行結(jié)束就開始執(zhí)行下一次操作, 同步執(zhí)行了多個(gè)轉(zhuǎn)場(chǎng)操作,這時(shí)就需要攔截控制器入棧\出棧的方法,確保當(dāng)有控制器正在進(jìn)行入棧\出棧的操作時(shí),沒有其他入棧\出棧操作。

實(shí)現(xiàn)

通過Runtime的方法魔法Method Swizzling技術(shù)實(shí)現(xiàn)。分類實(shí)現(xiàn)修改navigationControlle的pop和push方法。在push\pop方法中設(shè)置一個(gè)標(biāo)志位,動(dòng)畫結(jié)束之后,重置標(biāo)志位,通過標(biāo)志位來判斷push\pop操作是否執(zhí)行。代碼實(shí)現(xiàn)如下:

@interface UINavigationController () <UINavigationControllerDelegate>

@property (nonatomic, assign) BOOL viewTransitionInProgress;

@end

@implementation UINavigationController (SafeTransition)

+ (void)load {
    method_exchangeImplementations(class_getInstanceMethod(self, @selector(pushViewController:animated:)),
                                   class_getInstanceMethod(self, @selector(safePushViewController:animated:)));
    
    method_exchangeImplementations(class_getInstanceMethod(self, @selector(popViewControllerAnimated:)),
                                   class_getInstanceMethod(self, @selector(safePopViewControllerAnimated:)));
    
    method_exchangeImplementations(class_getInstanceMethod(self, @selector(popToRootViewControllerAnimated:)),
                                   class_getInstanceMethod(self, @selector(safePopToRootViewControllerAnimated:)));
    
    method_exchangeImplementations(class_getInstanceMethod(self, @selector(popToViewController:animated:)),
                                   class_getInstanceMethod(self, @selector(safePopToViewController:animated:)));
    
}

#pragma mark - setter & getter
- (void)setViewTransitionInProgress:(BOOL)property {
    NSNumber *number = [NSNumber numberWithBool:property];
    objc_setAssociatedObject(self, @selector(viewTransitionInProgress), number, OBJC_ASSOCIATION_RETAIN);
}

- (BOOL)viewTransitionInProgress {
    NSNumber *number = objc_getAssociatedObject(self, @selector(viewTransitionInProgress));
    return [number boolValue];
}

#pragma mark - Intercept Pop, Push, PopToRootVC
- (NSArray *)safePopToRootViewControllerAnimated:(BOOL)animated {
    if (self.viewTransitionInProgress) return nil;
    
    if (animated) {
        self.viewTransitionInProgress = YES;
    }
    
    NSArray *viewControllers = [self safePopToRootViewControllerAnimated:animated];
    if (viewControllers.count == 0) {
        self.viewTransitionInProgress = NO;
    }
    
    return viewControllers;
}

- (NSArray *)safePopToViewController:(UIViewController *)viewController animated:(BOOL)animated {
    if (self.viewTransitionInProgress) return nil;
    
    if (animated){
        self.viewTransitionInProgress = YES;
    }
    
    NSArray *viewControllers = [self safePopToViewController:viewController animated:animated];
    if (viewControllers.count == 0) {
        self.viewTransitionInProgress = NO;
    }
    
    return viewControllers;
}

- (UIViewController *)safePopViewControllerAnimated:(BOOL)animated {
    if (self.viewTransitionInProgress) return nil;
    
    if (animated) {
        self.viewTransitionInProgress = YES;
    }
    
    UIViewController *viewController = [self safePopViewControllerAnimated:animated];
    if (viewController == nil) {
        self.viewTransitionInProgress = NO;
    }
    
    return viewController;
}

- (void)safePushViewController:(UIViewController *)viewController animated:(BOOL)animated {
    if (self.viewTransitionInProgress == NO) {
        [self safePushViewController:viewController animated:animated];
        
        if (animated) {
            self.viewTransitionInProgress = YES;
        }
    }
}

@end

@implementation UIViewController (SafeTransitionLock)

+ (void)load {
    Method m1;
    Method m2;
    
    m1 = class_getInstanceMethod(self, @selector(safeViewDidAppear:));
    m2 = class_getInstanceMethod(self, @selector(viewDidAppear:));
    
    method_exchangeImplementations(m1, m2);
}

- (void)safeViewDidAppear:(BOOL)animated {
    self.navigationController.viewTransitionInProgress = NO;
    
    [self safeViewDidAppear:animated];
}

@end

最后編輯于
?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • 轉(zhuǎn)場(chǎng)過程解析 UINavigationController對(duì)于translation動(dòng)畫做了一定的封裝, 同時(shí)持有...
    Archerlly閱讀 6,838評(píng)論 24 10
  • 前言的前言 唐巧前輩在微信公眾號(hào)「iOSDevTips」以及其博客上推送了我的文章后,我的 Github 各項(xiàng)指標(biāo)...
    VincentHK閱讀 5,568評(píng)論 3 44
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,626評(píng)論 1 32
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,142評(píng)論 4 61
  • 其實(shí),閱讀《商君書》這樣的經(jīng)典文章,除了要了解當(dāng)時(shí)的社會(huì)背景、發(fā)展情況,更要結(jié)合當(dāng)時(shí)的社會(huì)實(shí)際來分析和把握,這樣才...
    健康體驗(yàn)閱讀 487評(píng)論 0 3

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