zoukankan      html  css  js  c++  java
  • iOS UIPageViewController系统方法崩溃修复

    一切始于这个神奇的报错

    Last Exception Backtrace:
    0   CoreFoundation                	0x1a3692a48 __exceptionPreprocess + 220 (NSException.m:199)
    1   libobjc.A.dylib               	0x1a33b9fa4 objc_exception_throw + 56 (objc-exception.mm:565)
    2   CoreFoundation                	0x1a35881c0 +[NSException raise:format:] + 108 (NSException.m:155)
    3   UIKitCore                     	0x1a7078bbc -[UIPageViewController _validatedViewControllersForTransitionWithViewControllers:animated:] + 512 (UIPageViewController.m:1223)
    4   UIKitCore                     	0x1a70796e0 -[UIPageViewController _setViewControllers:withCurlOfType:fromLocation:direction:animated:notifyDelegate:completion:] + 560 (UIPageViewController.m:1397)
    5   UIKitCore                     	0x1a707c4d8 -[UIPageViewController _handlePanGesture:] + 292 (UIPageViewController.m:0)
    6   UIKitCore                     	0x1a72f3630 -[UIGestureRecognizerTarget _sendActionWithGestureRecognizer:] + 48 (UIGestureRecognizer.m:129)
    7   UIKitCore                     	0x1a72fbd10 _UIGestureRecognizerSendTargetActions + 124 (UIGestureRecognizer.m:1282)
    8   UIKitCore                     	0x1a72f94a4 _UIGestureRecognizerSendActions + 352 (UIGestureRecognizer.m:1321)
    9   UIKitCore                     	0x1a72f8a04 -[UIGestureRecognizer _updateGestureForActiveEvents] + 652 (UIGestureRecognizer.m:0)
    10  UIKitCore                     	0x1a72ec998 _UIGestureEnvironmentUpdate + 2108 (UIGestureEnvironment.m:160)
    11  UIKitCore                     	0x1a72ec110 -[UIGestureEnvironment _deliverEvent:toGestureRecognizers:usingBlock:] + 372 (UIGestureEnvironment.m:1143)
    12  UIKitCore                     	0x1a72ebed4 -[UIGestureEnvironment _updateForEvent:window:] + 204 (UIGestureEnvironment.m:1112)
    13  UIKitCore                     	0x1a77416f8 -[UIWindow sendEvent:] + 3324 (UIWindow.m:2876)
    14  UIKitCore                     	0x1a771de2c -[UIApplication sendEvent:] + 344 (UIApplication.m:11171)
    

    作为系统控件而言, UIPageViewController着实不让人省心, 坑坑洼洼就罢, 还能通过外部曲线救国, 这次直接在内部方法崩溃了, 一时半会还没有复现的方法

    当然了, 要是没解决此问题的话, 也不会有这篇文章

    节约时间, 先上代码

    @implementation UIPageViewController (fix)
    + (void)fix {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            Method dstMethod = class_getInstanceMethod([UIPageViewController class], NSSelectorFromString(@"_setViewControllers:withCurlOfType:fromLocation:direction:animated:notifyDelegate:completion:"));
            Method srcMethod = class_getInstanceMethod(self, @selector(fix_setViewControllers:withCurlOfType:fromLocation:direction:animated:notifyDelegate:completion:));
            method_exchangeImplementations(dstMethod, srcMethod);
        });
    }
    
    - (void)fix_setViewControllers:(nullable NSArray<UIViewController *> *)viewControllers
                      withCurlOfType:(UIPageViewControllerTransitionStyle)type
                        fromLocation:(CGPoint)location
                           direction:(UIPageViewControllerNavigationDirection)direction
                            animated:(BOOL)animated
                      notifyDelegate:(BOOL)notifyDelegate
                          completion:(void (^ __nullable)(BOOL finished))completion {
        if (!viewControllers.count) return;
        
        [self fix_setViewControllers:viewControllers withCurlOfType:type fromLocation:location direction:direction animated:animated notifyDelegate:notifyDelegate completion:completion];
    }
    @end
    

    复现

    要不是没地方copy代码, 我怎么会考虑复现自己修bug呢?

    使用UIPageViewController的属性设置是UIPageViewControllerTransitionStylePageCurlUIPageViewControllerNavigationOrientationHorizontal

    已知这个问题是在翻页中出现的, 但等待翻页触发崩溃过于漫长, 太过费时, 甚至考虑准备弄个自动化测试

    毕竟我懒, 既然是翻页中出现的问题, 那么先排查翻页的过程

    - (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(nonnull UIViewController *)viewController
    
    - (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(nonnull UIViewController *)viewController
    

    页面前后翻时的Delegate, 添加代码, 直接输出控制台

    若干次尝试后...

    出现了一个诡异的事, 在我乱翻之下, 短时间内可能触发2次向前翻页

    再次尝试若干次...

    期望的行为是左右滑翻页, 但上下滑动也可能瞬间触发2次向前翻页且界面没有发生可见的变化, 触发2次前翻时大概率发生崩溃

    搞事

    根据苹果爸爸的崩溃日志, 最终方法发生在这里

    [UIPageViewController _validatedViewControllersForTransitionWithViewControllers:animated:]
    

    原因是空数组, 那就明显是ViewControllers:的参数出了问题

    既然都复现了, 也找到了方法, 剩下的就是复刻基本套路了

    for (hook系统方法; !搞定; hook上一层) {
        直接return || 换参数
    }
    

    最后在上一层[UIPageViewController _setViewControllers:withCurlOfType:fromLocation:direction:animated:notifyDelegate:completion:]这里直接return异常清空就行了

    后记

    理论上比较完备的测试是需要考虑Swift的情况, 想了想...还是留给勤快的有缘人好了

  • 相关阅读:
    [翻译]Webpack解惑
    Vue.js与angular在数据实现的思考
    多线程入门-第三章-线程的调度与控制之优先级
    多线程入门-第二章-线程的生命周期
    多线程入门-第一章-线程的创建与启动
    多线程入门-概述
    IO流入门-第十三章-File相关
    IO流入门-第十二章-ObjectInputStream_ObjectOutputStream
    IO流入门-第十一章-PrintStream_PrintWriter
    IO流入门-第十章-DataInputStream_DataOutputStream
  • 原文地址:https://www.cnblogs.com/Simon-X/p/13034913.html
Copyright © 2011-2022 走看看