【问题标题】:Failed to block main thread with runloop on iOS15 with device iPhone12?无法使用设备 iPhone 12 在 iOS 15 上使用运行循环阻止主线程?
【发布时间】:2021-09-24 09:04:59
【问题描述】:

在我的项目中,我使用runloop阻塞主线程来实现获取UIAlertController的用户点击结果。这里是测试 Viewcontroller.h 和 Viewcontroller.m 代码:​​

#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@end


#import "ViewController.h"
@interface ViewController ()    
@end

static NSInteger kButtonIndex = -1;

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    BOOL result = [self test];
    NSLog(@"Result: %d",result);
}

- (BOOL)test{
    kButtonIndex = -1;
    
    NSLog(@"000");

    UIAlertController *alerVC = [UIAlertController alertControllerWithTitle:@"Alert" message:@"Message" preferredStyle:UIAlertControllerStyleAlert];
    [alerVC addAction:[UIAlertAction actionWithTitle:@"Ok" style:(UIAlertActionStyleDestructive) handler:^(UIAlertAction * _Nonnull action) {
        kButtonIndex = 1;
    }]];
    [alerVC addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
        kButtonIndex = 0;
    }]];

    NSLog(@"111");

    [self presentViewController:alerVC animated:YES completion:^{}];

    NSLog(@"222");

    while (kButtonIndex == -1) {
        [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1f]];
    }

    if (kButtonIndex == 1) {
        return YES;
    }
    if (kButtonIndex == 0) {
        return NO;
    }

    return NO;
}
@end

在 iPhone 11 或任何 iOS 版本的旧设备上,一旦你点击屏幕,UIAlertViewController 将显示,如果你点击 Ok 按钮,控制台日志应该是这样的:

2021-09-24 16:51:04.146063+0800 TestAlert[1520:46901] 000
2021-09-24 16:51:04.148558+0800 TestAlert[1520:46901] 111
2021-09-24 16:51:04.165442+0800 TestAlert[1520:46901] 222
2021-09-24 16:51:05.774194+0800 TestAlert[1520:46901] Result: 1

但如果在 iPhone 12 或更高版本的 iOS 15 设备上,一旦点击屏幕,UIAlertViewController 将不会显示,控制台日志应该是这样的:

2021-09-24 16:51:04.146063+0800 TestAlert[1520:46901] 000
2021-09-24 16:51:04.148558+0800 TestAlert[1520:46901] 111
2021-09-24 16:51:04.165442+0800 TestAlert[1520:46901] 222

似乎下面的代码不再起作用了:

while (kButtonIndex == -1) {
    [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1f]];
}

有人遇到过这个问题吗?谢谢!

额外: 我们的项目使用了Cordova和UIWebView,这个应用中的一些远程h5页面来自其他公司(如果我们将webview更改为WKWebView,这些公司需要更新他们的js文件,如cordova.js,这是一项艰巨的工作,所以我们留在UIWebView )...在UIWebView中,有用户说js confirm()函数在iOS15上不再显示模态视图,我尝试了很多,发现UIWebView中的js confirm()函数出现了一个UIAlertController..我尝试重写UIWebView 确认这样的实现,它在除 iPhone 12 iOS15 之外的任何设备上都能正常工作:

#import <UIKit/UIKit.h>
@interface UIWebView (Alert)
@end

#import "UIWebView+Alert.h"

@implementation UIWebView (Alert)

static NSInteger kButtonIndex = -1;

- (BOOL)webView:(UIWebView *)sender runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(id)frame{
    kButtonIndex = -1;

    UIAlertController *alerVC = [UIAlertController alertControllerWithTitle:@"Alert" message:message preferredStyle:UIAlertControllerStyleAlert];
    [alerVC addAction:[UIAlertAction actionWithTitle:@"Ok" style:(UIAlertActionStyleDestructive) handler:^(UIAlertAction * _Nonnull action) {
        kButtonIndex = 1;
    }]];
    [alerVC addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
        kButtonIndex = 0;
    }]];

    [[self topViewController] presentViewController:alerVC animated:YES completion:^{}];

    while (kButtonIndex == -1) {
        [[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1f]];
    }

    if (kButtonIndex == 1) {
        return YES;
    }
    if (kButtonIndex == 0) {
        return NO;
    }

    return NO;
}

@end

实际上,上面的代码和我问题中最前面的代码几乎相同,如果有人可以提供帮助,您可以运行最前面的测试代码来重现问题。

【问题讨论】:

  • 为什么要把asynchrone改成synchrone?为什么不处理异步呢?
  • 为什么要阻塞主线程?只是异步工作
  • @Paulw11 我在我的问题中添加了一些额外的注释,很难解决????
  • @Larme 我在我的问题中添加了一些额外的注释,很难解决????

标签: ios nsrunloop


【解决方案1】:

我们在 iPhone 12 上的 iOS 15 上遇到了同样的动画问题。我们使用 [[NSRunLoop mainRunLoop] runUntilDate] 允许动画完成执行,然后继续在主线程中执行代码。但是,这在 iPhone 12 上的 iOS 15 上不起作用。即使调用 runUntilDate 的时间超过动画持续时间,动画也不会完成。这似乎只是运行 iOS 15 的 iPhone 12 设备上的问题。在模拟器、iPhone 11 设备上,甚至在与 iPhone 12 具有相同处理器的 iPad Air 4 上,一切正常。似乎 UI 代码会通常在调用 runUntilDate 时在主线程上执行在 iOS 15 / iPhone 12 上不会这样做。

【讨论】:

  • 好像是系统bug,最后我们避免调用js的confirm()函数,通过js中的另一个自定义实现显示popup view来解决这个问题。
  • 感谢您的跟进。同意这似乎是 iOS 15 的一个问题。我们一直在通过迁移所有遇到问题的东西来解决问题,以使用块 + 完成处理程序与 [NSRunLoop runUntilDate]
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-12-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多