• 主线程阻塞导致的事件传递混乱


    公司某个ios产品代码里面,在启动过程当中,有个看起来很怪异的逻辑。

    先说一下启动的基本过程中,首先window的rootViewController设置为一个活动图FlashViewController:

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
       ...
       self.window.rootViewController = [[FlashViewController alloc] init];
       ...
    }
    

    如果用户点击活动图或者过一两秒,再将rootViewController设置为主视图MainViewController:

    - (void)onFlashViewControllerComplete {
        ...
        self.window.userInteractionEnabled = NO;
        self.window.rootViewController = [[MainViewController alloc] init];
        ...
    }
    

    在MainViewController里面:

    - (void)viewDidAppear {
       ...
       self.view.window.userInteractionEnabled = YES;
       ...
    }
    

    读到这里应该已经发现了这个怪异的逻辑,切换rootViewController之前, window被设置成不接受点击事件,在主视图显示之后再恢复。从相关同事哪里了解到,这个逻辑要解决的问题是,如果用户在活动图里面疯狂点击,那么点击事件会传递给了MainViewController,进而造成意外情况。

    经过一些测试,我推测原因应该是这样的,ios系统里面用户事件的优先级很高,不管应用在干什么,只要用户点击屏幕,系统会为应用生成事件并放到事件队列里面,如果应用的主线程被阻塞了,那么事件队列里面就可能积压很多的事件,当主线程空出来之后才得到处理。

    问题就出在这个时间差上面,在上面的case中,但由于切换rootViewController阻塞了线程,用户感觉点击的目标是FashViewController所代表的界面,实际上部分事件发送给了下一个界面MainViewController。这个问题很容易重现,只要往navigationController里面push一个controller,在后者的loadView里面执行一个很耗时间的操作,比如分配100000个对象,然后点击屏幕就能复现。

    解决方法1

    产品当前的方法基本上能解决这个问题(当主线程空出来的时候,window不接受事件,于是事件被忽略掉),但是不够彻底,viewDidAppear并不代表渲染已经完成,只要再延迟一两个runloop就好了,在MainViewController里面:

    - (void)viewDidAppear {
       ...
       if (!self.view.window.userInteractionEabled) {
    	dispatch_async(dispatch_get_main_queue(),^{
    	   self.view.window.userInteractionEnable = YES;
    	})
       }
       ...
    }
    

    这个方法的一个缺陷是将这个逻辑扩散到两个地方,不利于维护

    解决方法2

    避免切换rootviewcontroller,一启动就将MainViewController设置为rootViewController,把FlashViewcController.view覆盖到window上;这样的实际是启动时将两个controller一起渲染,缺点是MainViewController的viewDidAppear会提前;公司另外有两个产品(当时还不知道有这个问题)用的就是这个方案,所以从来没有这个问题

     - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
        ...
        self.window.rootViewController = [[MainViewController alloc] init];
        FlashViewController *flash = [[FlashViewController alloc] init];
        flash.view.frame = self.window.bounds;
        [self.window addSubview:flash.view];
        ...
    }  
    
  • 相关阅读:
    BZOJ 1597: [Usaco2008 Mar]土地购买
    BZOJ 1005: [HNOI2008]明明的烦恼
    BZOJ 1004: [HNOI2008]Cards
    Burnside引理和Polya定理
    BZOJ 1003: [ZJOI2006]物流运输
    BZOJ 1002: [FJOI2007]轮状病毒
    BZOJ 1001: [BeiJing2006]狼抓兔子
    网络流 最大流dinic算法解释
    51nod 1299 监狱逃离
    2017.11.26【清华集训2017】模拟
  • 原文地址:https://www.cnblogs.com/longhuihu/p/4032638.html
Copyright © 2020-2023  润新知