iOS应用拒绝通讯录权限时崩溃问题求助
iOS通讯录权限拒绝时应用偶尔崩溃问题
开发的iOS应用在用户登录时请求通讯录权限,点击「允许」时功能正常,但点击「不允许」时应用偶尔崩溃,崩溃位置在创建UIAlertController的代码处,报错提示:
Assert Cannot be called with asCopy = NO on non-main thread
相关代码
- (BOOL) checkContactPermission { __block BOOL authorized = NO; // VAMOS A SOPORTAR A PARTIR DE IOS 9 POR LO QUE NO NECESITAMOS COMPROBARLO // if ([CNContactStore class]) { }; //ios9 or later CNEntityType entityType = CNEntityTypeContacts; // Si no hemos pedido permiso de acceso antes lo pedimos if([CNContactStore authorizationStatusForEntityType:entityType] == CNAuthorizationStatusNotDetermined) { CNContactStore * contactStore = [[CNContactStore alloc] init]; [contactStore requestAccessForEntityType:entityType completionHandler:^(BOOL granted, NSError * _Nullable error) { if(granted){ NSLog(@"Contacts access Granted"); authorized = YES; } // Si el usuario no nos da permiso mostramos un alert else { NSLog(@"Contacts access Denied"); UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"No se puede acceder a los contactos" message:@"La App necesita permiso de acceso a los contactos para mostrar el nombre de cada contacto en el listado de llamadas de la app." preferredStyle: UIAlertControllerStyleAlert]; UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action){ // Ok action example }]; [alert addAction:okAction]; // Muestro el alert en el hilo principal dispatch_async(dispatch_get_main_queue(), ^{ [[[[UIApplication sharedApplication]keyWindow]rootViewController]presentViewController:alert animated:YES completion:nil]; }); } }]; } else if( [CNContactStore authorizationStatusForEntityType:entityType] == CNAuthorizationStatusAuthorized) { NSLog(@"Contacts access Authorized"); authorized = YES; } return authorized; }
Xcode线程信息
正常情况(OK)-> Thread 25
Thread 25 Queue : com.apple.root.default-qos (concurrent)#0 0x00000001034c29d1 in __43-[GetContactsPlugin checkContactPermission]_block_invoke at /Users/vyorkmur/Documents/APP MiJazztel/Alicante/cordova10/mijazztel-application-typescript/platforms/ios/Mi Jazztel/Plugins/com.viewnext.plugin.getcontacts/GetContactsPlugin/GetContactsPlugin.m:198崩溃情况(KO)-> Thread 4
Thread 4 Queue : com.apple.root.default-qos (concurrent)#23 0x0000000108340a19 in __43-[GetContactsPlugin checkContactPermission]_block_invoke at /Users/vyorkmur/Documents/APP MiJazztel/Alicante/cordova10/mijazztel-application-typescript/platforms/ios/Mi Jazztel/Plugins/com.viewnext.plugin.getcontacts/GetContactsPlugin/GetContactsPlugin.m:200
问题原因
CNContactStore的权限请求回调completionHandler运行在后台线程,而UIKit的所有操作(包括创建UIAlertController、UIAlertAction这类UI组件)必须在主线程执行。当前代码仅把弹窗展示操作放到主线程,但弹窗的创建、配置过程仍在后台线程,触发了系统断言导致崩溃。
解决方案
将弹窗的创建、配置及展示的全部逻辑,都移至dispatch_async(dispatch_get_main_queue(), ^{...})代码块内,确保所有UI操作都在主线程执行。
修改后的核心代码片段:
[contactStore requestAccessForEntityType:entityType completionHandler:^(BOOL granted, NSError * _Nullable error) { if(granted){ NSLog(@"Contacts access Granted"); authorized = YES; } // Si el usuario no nos da permiso mostramos un alert else { NSLog(@"Contacts access Denied"); // 所有UI操作移至主线程 dispatch_async(dispatch_get_main_queue(), ^{ UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"No se puede acceder a los contactos" message:@"La App necesita permiso de acceso a los contactos para mostrar el nombre de cada contacto en el listado de llamadas de la app." preferredStyle: UIAlertControllerStyleAlert]; UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action){ // Ok action example }]; [alert addAction:okAction]; [[[[UIApplication sharedApplication]keyWindow]rootViewController]presentViewController:alert animated:YES completion:nil]; }); } }];
内容的提问来源于stack exchange,提问作者Victor York
相关产品推荐
相关产品推荐

