在NSURLSession完成回调中调用WKWebView loadRequest的主线程问题
搞定WKWebView必须在主线程执行的问题
嘿,这个问题我之前也踩过坑!核心就是WKWebView的所有UI操作都必须在主线程跑,而NSURLSession的completion handler默认是在后台线程触发的,直接在里面调用loadRequest肯定会出问题。你换成NSURLSession替代废弃的NSURLConnection是完全正确的,我来帮你梳理下解决方案和代码优化点:
问题本质
WKWebView是UI组件,苹果明确要求所有UI相关方法(比如loadRequest:)都得在主线程执行。你之前用的NSURLConnection sendSynchronousRequest如果是在主线程调用的,那后续操作自然在主线程,所以没毛病,但这个API已经被苹果废弃,用NSURLSession是正确的迁移方向。
你的代码优化建议
你的代码已经想到用dispatch_async切到主队列,这点是对的,但有几个容易踩的小坑和可以优化的地方:
- 原代码里没调用
[task resume]!NSURLSession的任务创建后默认是挂起状态,必须调用resume才会发起请求,这是极其容易漏掉的关键操作! - URL格式写错了:
http:/127.0.0.1/少了一个斜杠,应该是http://127.0.0.1/,不然URL解析会直接失败。 - 重复创建相同的URL和请求,可以复用变量减少冗余代码。
- 存在无限递归重试的风险:如果服务器一直不可达,
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
相关产品推荐
相关产品推荐

