You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在NSURLSession完成回调中调用WKWebView loadRequest的主线程问题

搞定WKWebView必须在主线程执行的问题

嘿,这个问题我之前也踩过坑!核心就是WKWebView的所有UI操作都必须在主线程跑,而NSURLSession的completion handler默认是在后台线程触发的,直接在里面调用loadRequest肯定会出问题。你换成NSURLSession替代废弃的NSURLConnection是完全正确的,我来帮你梳理下解决方案和代码优化点:

问题本质

WKWebView是UI组件,苹果明确要求所有UI相关方法(比如loadRequest:)都得在主线程执行。你之前用的NSURLConnection sendSynchronousRequest如果是在主线程调用的,那后续操作自然在主线程,所以没毛病,但这个API已经被苹果废弃,用NSURLSession是正确的迁移方向。

你的代码优化建议

你的代码已经想到用dispatch_async切到主队列,这点是对的,但有几个容易踩的小坑和可以优化的地方:

  1. 原代码里没调用[task resume]!NSURLSession的任务创建后默认是挂起状态,必须调用resume才会发起请求,这是极其容易漏掉的关键操作!
  2. URL格式写错了:http:/127.0.0.1/少了一个斜杠,应该是http://127.0.0.1/,不然URL解析会直接失败。
  3. 重复创建相同的URL和请求,可以复用变量减少冗余代码。
  4. 存在无限递归重试的风险:如果服务器一直不可达,tryServePage会一直调用自己,最好加个重试次数限制避免栈溢出。

优化后的代码如下:

- (void)tryServePageWithRetryCount:(NSInteger)retryCount {
    // 修正URL格式,复用这个URL变量
    NSString *localNodeServerURL = @"http://127.0.0.1/";
    NSURL *url = [NSURL URLWithString:localNodeServerURL];
    
    // 用sharedSession更合理,不用每次alloc新的NSURLSession
    NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData * _Nullable myData, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        // 更严谨的可达性判断:不仅要看data,还要检查error
        BOOL reachable = (myData != nil && error == nil);
        
        // 必须切回主线程操作WKWebView
        dispatch_async(dispatch_get_main_queue(), ^{
            if (!reachable) {
                // 限制最多重试3次,避免无限递归
                if (retryCount < 3) {
                    NSLog(@"服务器不可达,正在重试第%ld次", (long)retryCount + 1);
                    [self tryServePageWithRetryCount:retryCount + 1];
                } else {
                    // 重试耗尽,这里可以加个错误提示或者加载离线页面
                    NSLog(@"无法连接到本地服务器,重试次数已用完");
                }
            } else {
                NSURLRequest *request = [NSURLRequest requestWithURL:url];
                [self->_myWebView loadRequest:request];
            }
        });
    }];
    
    // 关键!启动任务
    [task resume];
}

// 对外调用的入口方法,初始重试次数传0
- (void)startServingPage {
    [self tryServePageWithRetryCount:0];
}

几个关键提醒

  • 主线程切换:所有涉及WKWebView的操作,不管是loadRequest还是修改它的属性,都要放在dispatch_async(dispatch_get_main_queue(), ^{ ... })里面,别用dispatch_sync,不然会阻塞后台线程拖慢性能。
  • NSURLSession的使用:尽量复用NSURLSession sharedSession或者自定义的session,不要每次创建新的,避免不必要的资源浪费。
  • 错误处理:除了判断myData,还要检查error,有时候网络请求失败会返回error但没有data,这样判断更准确。

之前用NSURLConnection同步请求没问题,是因为同步请求会阻塞当前线程(如果是主线程的话),后续的loadRequest自然在主线程,但这种方式会卡住UI,用户体验很差,所以用NSURLSession的异步任务+主线程切换是更优的方案。

内容的提问来源于stack exchange,提问作者Michael Angelo Ruta

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 18:47:28