zoukankan      html  css  js  c++  java
  • 主线程阻塞导致的事件传递混乱

    公司某个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];
        ...
    }  
    
  • 相关阅读:
    jconsole远程连接监控tomcat
    Nginx监控配置
    Nginx安装+2tomcat配置
    Flexbox布局模式的理解
    Web性能优化:图片优化
    大公司里怎样开发和部署前端代码?
    原生js下拉刷新
    全国三级城市联动 js版
    js获取智能机浏览器版本信息
    JS年月日三级联动下拉框日期选择代码
  • 原文地址:https://www.cnblogs.com/longhuihu/p/4032638.html
Copyright © 2011-2022 走看看