This method can cause UI unresponsiveness if invoked on the main thread.
Xcode编译运行报错:This method can cause UI unresponsiveness if invoked on the main thread. Instead, consider waiting for the `-locationManagerDidChangeAuthorization:` callback and checking `authorizationStatus` first.
问题根源
这个警告的核心意思是:你正在主线程(UI线程)上同步地检查定位权限授权状态(authorizationStatus
),而这个操作可能会因为需要与底层硬件或系统进程通信而产生延迟,从而阻塞主线程,导致界面卡顿、无响应。
在 iOS 中,所有UI的绘制和交互都发生在主线程。如果主线程被长时间阻塞,用户就会感觉到应用“卡住了”。
解决方案
正确的做法是采用异步的方式来获取和响应授权状态的变化。主要有以下两种方法:
方法一:使用 locationManagerDidChangeAuthorization:
委托回调(推荐,iOS 14+)
这是 Apple 在现代 iOS 版本中推荐的方式。你需要实现 CLLocationManagerDelegate
中的这个新方法,它会在应用的定位权限发生任何变化时被调用(例如用户首次选择、在系统设置中更改等)。
步骤如下:
设置委托(Delegate):确保你的
CLLocationManager
实例设置了delegate
。实现回调方法:在委托对象中实现
locationManagerDidChangeAuthorization:
方法。在回调中处理逻辑:在这个方法内部,根据当前的授权状态来执行你的后续操作(如开始更新位置、显示地图等)。
代码示例:
#import <CoreLocation/CoreLocation.h>
#import <UIKit/UIKit.h>
#import "HDLocatoinViewController.h"@interface HDLocatoinViewController ()<CLLocationManagerDelegate>@property (strong, nonatomic) CLLocationManager *locationManager;@end@implementation HDLocatoinViewController- (void)viewDidLoad {[super viewDidLoad];// 初始化位置管理器并设置代理self.locationManager = [[CLLocationManager alloc] init];self.locationManager.delegate = self;// 不要在这里直接检查 authorizationStatus// 可以在这里请求授权(如果需要),但要先检查locationServiceEnable,也就是定位服务总开关有没有打开// [self locationAuthorization];
}- (void)locationAuthorization{// 在主线程上异步检查定位服务dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{BOOL locationServicesEnabled = [CLLocationManager locationServicesEnabled];dispatch_async(dispatch_get_main_queue(), ^{if (locationServicesEnabled == NO) {[self showLocationServicesDisabledAlert];} else {[self.locationManager requestWhenInUseAuthorization];}});});
}#pragma mark - CLLocationManagerDelegate- (void)locationManagerDidChangeAuthorization:(CLLocationManager *)manager {// 在授权状态变化时安全地检查if (@available(iOS 14.0, *)) {[self handleLocationAuthorization:manager.authorizationStatus];}
}// 为了兼容 iOS 13 及以下版本,也可以实现旧版代理方法
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {if (@available(iOS 14.0, *)) {// 在 iOS 14+ 上,使用新的 locationManagerDidChangeAuthorization: 方法return;}[self handleLocationAuthorization:status];
}- (void)handleLocationAuthorization:(CLAuthorizationStatus)status {switch (status) {case kCLAuthorizationStatusAuthorizedAlways:case kCLAuthorizationStatusAuthorizedWhenInUse:// 权限已授予,开始定位NSLog(@"定位权限已授予,开始更新位置...");[self.locationManager startUpdatingLocation];break;case kCLAuthorizationStatusDenied:case kCLAuthorizationStatusRestricted:// 权限被拒绝或受限NSLog(@"定位权限被拒绝");[self showPermissionAlert];break;case kCLAuthorizationStatusNotDetermined:// 尚未决定,通常不会在这里触发NSLog(@"尚未请求权限");break;default:break;}
}- (void)showLocationServicesDisabledAlert {UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"定位服务已关闭"message:@"请前往「设置」→「隐私与安全性」→「定位服务」中开启定位功能,以便使用此应用的位置服务"preferredStyle:UIAlertControllerStyleAlert];// 取消按钮UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消"style:UIAlertActionStyleCancelhandler:nil];// 前往设置按钮UIAlertAction *settingsAction = [UIAlertAction actionWithTitle:@"前往设置"style:UIAlertActionStyleDefaulthandler:^(UIAlertAction * _Nonnull action) {// 跳转到系统的定位服务设置页面NSURL *settingsURL = [NSURL URLWithString:UIApplicationOpenSettingsURLString];if (@available(iOS 10.0, *)) {[[UIApplication sharedApplication] openURL:settingsURL options:@{} completionHandler:nil];} else {// 兼容 iOS 10 以下版本[[UIApplication sharedApplication] openURL:settingsURL];}}];[alert addAction:cancelAction];[alert addAction:settingsAction];[self presentViewController:alert animated:YES completion:nil];
}- (void)showPermissionAlert {// 提示用户去设置中开启权限UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"需要定位权限"message:@"请前往「设置」->「隐私与安全性」->「定位服务」中允许本应用使用定位"preferredStyle:UIAlertControllerStyleAlert];UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil];UIAlertAction *settingsAction = [UIAlertAction actionWithTitle:@"去设置" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {NSURL *settingsURL = [NSURL URLWithString:UIApplicationOpenSettingsURLString];if ([[UIApplication sharedApplication] canOpenURL:settingsURL]) {[[UIApplication sharedApplication] openURL:settingsURL options:@{} completionHandler:nil];}}];[alert addAction:cancelAction];[alert addAction:settingsAction];[self presentViewController:alert animated:YES completion:nil];
}#pragma mark - 用户操作触发请求授权- (IBAction)requestLocationPermissionButtonTapped:(id)sender {// 当用户执行需要定位功能的操作时请求授权[self locationAuthorization];
}@end
方法二:在适当的时机请求授权,而非直接检查状态
很多时候,你并不需要主动去“检查”状态,而是应该在用户执行某个需要定位功能的操作时,去“请求”授权。
场景:用户点击了一个“获取当前位置”的按钮。
做法:直接在按钮的
@IBAction
方法中调用requestWhenInUseAuthorization()
。如果权限已经是.authorized
,这个请求会被系统忽略;如果还没决定,系统会弹出提示框;如果已被拒绝,你可以在locationManagerDidChangeAuthorization:
中处理(如方法一所示)。
总结与最佳实践
避免同步检查:永远不要在
viewDidLoad
或其他主线程方法中直接使用CLLocationManager.authorizationStatus()
来决定UI流程。拥抱异步回调:依赖
locationManagerDidChangeAuthorization:
这个委托方法来响应权限状态的变化。这是最安全、最现代的方式。按需请求:在用户意图明确(如点击相关按钮)时再请求定位权限,这样系统提示框的出现对用户来说更合情合理,通过率也更高。
处理所有状态:在你的代码中妥善处理
.notDetermined
(未决定)、.authorized
(已授权)、.denied
(已拒绝)等所有可能的授权状态,提供良好的用户引导。
好的,解决方案知道了,现在问题是这个报报错到底是哪行代码产生的,因为一个项目中可能有很多模块都有定位功能权限的调用,所以我们现在要查找问题代码产生行
步骤如下:
第一步:点击Xcode左侧工具栏上的警告图标,如下图
第二步:点击左下角的过滤图标,并选择“ISSUE KIND Build”,如下图
第三步:点击Build在弹出的列表中选择Run,并将右下角的蓝色小圆叉点击一下,置灰,如下图
第四步:第三步结束后就能看到过滤内容区显示了警告的具体原因,可以点击小三角展开查看具体代码行,如下图
奇迹出现了,报错代码行产生了,如下图
这里明显能看出是极光推送模块中JCommonCollectLocationManager这个类里面调用了定位权限代码导致的警告,升级极光推送SDK,解决问题!