多次Push Pop导致的`Can't add self as subview`问题

  • 崩溃堆栈
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Can't add self as subview'
*** First throw call stack:
(
    0   CoreFoundation                      0x000000010ec2df35 __exceptionPreprocess + 165
    1   libobjc.A.dylib                     0x000000010e8c8bb7 objc_exception_throw + 45
    2   CoreFoundation                      0x000000010ec2de6d +[NSException raise:format:] + 205
    3   UIKit                               0x000000010b351982 -[UIView(Internal) _addSubview:positioned:relativeTo:] + 123
    4   UIKit                               0x000000010b2d0b19 __53-[_UINavigationParallaxTransition animateTransition:]_block_invoke + 1814
    5   UIKit                               0x000000010b34c5ce +[UIView(Animation) performWithoutAnimation:] + 65
    6   UIKit                               0x000000010b2d0072 -[_UINavigationParallaxTransition animateTransition:] + 1225
    7   UIKit                               0x000000010b424e6c -[UINavigationController _startCustomTransition:] + 3038
    8   UIKit                               0x000000010b4303fe -[UINavigationController _startDeferredTransitionIfNeeded:] + 386
    9   UIKit                               0x000000010b430f47 -[UINavigationController __viewWillLayoutSubviews] + 43
    10  UIKit                               0x000000010b576509 -[UILayoutContainerView layoutSubviews] + 202
    11  UIKit                               0x000000010b354973 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 521
    12  QuartzCore                          0x000000010f589de8 -[CALayer layoutSublayers] + 150
    13  QuartzCore                          0x000000010f57ea0e _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 380
    14  QuartzCore                          0x000000010f57e87e _ZN2CA5Layer28layout_and_display_if_neededEPNS_11TransactionE + 24
    15  QuartzCore                          0x000000010f4ec63e _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 242
    16  QuartzCore                          0x000000010f4ed74a _ZN2CA11Transaction6commitEv + 390
    17  UIKit                               0x000000010b2d814d _UIApplicationHandleEventQueue + 2035
    18  CoreFoundation                      0x000000010eb63551 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
    19  CoreFoundation                      0x000000010eb5941d __CFRunLoopDoSources0 + 269
    20  CoreFoundation                      0x000000010eb58a54 __CFRunLoopRun + 868
    21  CoreFoundation                      0x000000010eb58486 CFRunLoopRunSpecific + 470
    22  GraphicsServices                    0x0000000110c659f0 GSEventRunModal + 161
    23  UIKit                               0x000000010b2db420 UIApplicationMain + 1282
    24  moma-beta                           0x00000001071abe1f main + 111
    25  libdyld.dylib                       0x000000011068f145 start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException

  • 复现步骤
push pop.gif
  • 修复方案: Hook UINavigationController

- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated;

- (nullable UIViewController *)popViewControllerAnimated:(BOOL)animated;

两个方法。

  • Code
   @implementation UINavigationController (PushPopCrashFix)

   + (void)load {
       static dispatch_once_t onceToken;
       dispatch_once(&onceToken, ^{
           SwizzledMethod(self, self, @selector(popViewControllerAnimated:), @selector(p_PopViewControllerAnimated:));
           SwizzledMethod(self, self, @selector(pushViewController:animated:), @selector(p_PushViewController:animated:));
       });
   }

   - (BOOL)prohibitPushPop {
       return [objc_getAssociatedObject(self, @selector(prohibitPushPop)) boolValue];
   }

   - (void)setProhibitPushPop:(BOOL) prohibitPushPop {
       objc_setAssociatedObject(self, @selector(prohibitPushPop), @(prohibitPushPop), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
   }

   - (UIViewController *)p_FixPopViewControllerAnimated:(BOOL)animated {
       if (self.prohibitPushPop) {
           return nil;
       }

       self.prohibitPushPop = YES;
       UIViewController *vc = [self DPPFixPopViewControllerAnimated:animated];
       [CATransaction setCompletionBlock:^{
           self.prohibitPushPop = NO;
       }];
       return vc;
}

   - (void)p_PushViewController:(UIViewController *)viewController animated:(BOOL)animated {
       if (self.prohibitPushPop) {
           return;
       }

       self.prohibitPushPop = YES;
       [self DPPFixPushViewController:viewController animated:animated];
       [CATransaction setCompletionBlock:^{
           self.prohibitPushPop = NO;
       }];
}

   @end

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容