Informe de locking cuando el usuario accede a la libreta de direcciones

En mi aplicación, Crashlytics se utiliza para recostackr informes de fallos de los usuarios. Aquí hay un informe de falla de un usuario. Es posible que dependa de la información de contactos del usuario. No puedo volver a crear el crash, ya que no sé qué hay en sus contactos. ¿Alguien tiene una idea sobre esta situación?

com.apple.root.default-priority Crashed 0 CoreFoundation CFStringCreateCopy + 13 1 AppSupport CPSqliteDatabaseCreateWithPath + 36 2 AppSupport CPSqliteDatabaseCreateWithPath + 36 3 AppSupport CPRecordStoreGetDatabase + 16 4 AppSupport _getReaderConnection + 10 5 AppSupport CPRecordStoreProcessQueryWithBindBlock + 22 6 AppSupport CPRecordStoreCopyAllInstancesOfClassWhereWithBindBlock + 98 7 AddressBook ABCCopyArrayOfAllPeopleInSourceWithSortOrdering + 244 8 SeeYouKee PhoneNumberInputViewController.m line 538-[PhoneNumberInputViewController dofetchContacts:] 9 AddressBook __37-[ABTCC accessRequestWithCompletion:]_block_invoke_0 + 26 10 TCC __TCCAccessRequest_block_invoke_038 + 316 11 ... libxpc.dylib _xpc_connection_call_reply + 26 12 libdispatch.dylib _dispatch_root_queue_drain + 278 13 libdispatch.dylib _dispatch_worker_thread2 + 92 14 libsystem_c.dylib _pthread_wqthread + 360 

El código para 8 SeeYouKee PhoneNumberInputViewController.m line 538-[PhoneNumberInputViewController dofetchContacts:] es:

 NSArray *contactsInAddressBook = CFBridgingRelease(ABAddressBookCopyArrayOfAllPeopleInSourceWithSortOrdering(addressBook, nil, kABPersonSortByLastName)); 

EDIT 1

 -(void)dofetchContacts:(ABAddressBookRef)addressBook{ NSMutableArray *contactMutArr = [NSMutableArray array]; NSMutableString *mStrOfContacts = [NSMutableString string]; NSArray *contactsInAddressBook = CFBridgingRelease(ABAddressBookCopyArrayOfAllPeopleInSourceWithSortOrdering(addressBook, nil, kABPersonSortByLastName)); if (ABPersonGetCompositeNameFormat() == kABPersonCompositeNameFormatLastNameFirst) { for (id aPerson in contactsInAddressBook) { ABRecordRef person = (__bridge ABRecordRef)(aPerson); ABMultiValueRef phoneMultiValue = ABRecordCopyValue(person, kABPersonPhoneProperty); ABMultiValueRef emailMultiValue = ABRecordCopyValue(person, kABPersonEmailProperty); int countPhone = 0; int countEmail = 0; NSMutableArray *phoneStrArr; NSMutableArray *emailStrArr; if (phoneMultiValue != NULL) { countPhone = ABMultiValueGetCount(phoneMultiValue); } if (emailMultiValue != NULL) { countEmail = ABMultiValueGetCount(emailMultiValue); } if (countEmail>0) { emailStrArr = [NSMutableArray array]; for (int i = 0; i < countEmail; i++) { CFStringRef anEmailCF = ABMultiValueCopyValueAtIndex(emailMultiValue, i); NSString *anEmail = (__bridge NSString *)anEmailCF; [emailStrArr addObject:anEmail]; if (anEmailCF != NULL)CFRelease(anEmailCF); } } if (countPhone > 0) { phoneStrArr = [NSMutableArray array]; for (int i = 0; i < countPhone; i++) { CFStringRef anPhoneCF = ABMultiValueCopyValueAtIndex(phoneMultiValue, i); NSString *anPhone = (__bridge NSString *)anPhoneCF; NSCharacterSet *cs = [[NSCharacterSet characterSetWithCharactersInString:@"0123456789"] invertedSet]; NSString *anPhonePureNumber = [[anPhone componentsSeparatedByCharactersInSet:cs] componentsJoinedByString:@""]; [phoneStrArr addObject:anPhonePureNumber]; if (anPhoneCF != NULL)CFRelease(anPhoneCF); } } // if (arrRefOfEmails != NULL)CFRelease(arrRefOfEmails); CFStringRef lastNameMultiValueCF = ABRecordCopyValue(person, kABPersonLastNameProperty); CFStringRef firstNmaeMultiValueCF = ABRecordCopyValue(person, kABPersonFirstNameProperty); CFStringRef middleNmaeMultiValueCF = ABRecordCopyValue(person, kABPersonMiddleNameProperty); NSString *lastNameMultiValue = (__bridge NSString *)lastNameMultiValueCF; NSString *firstNmaeMultiValue = (__bridge NSString *)firstNmaeMultiValueCF; NSString *middleNmaeMultiValue = (__bridge NSString *)middleNmaeMultiValueCF; NSString *name = [NSString stringWithFormat:@"%@%@%@",(![lastNameMultiValue length])?@"":lastNameMultiValue, (![middleNmaeMultiValue length])?@"":middleNmaeMultiValue, (![firstNmaeMultiValue length])?@"":firstNmaeMultiValue]; if (lastNameMultiValueCF != NULL)CFRelease(lastNameMultiValueCF); if (firstNmaeMultiValueCF != NULL)CFRelease(firstNmaeMultiValueCF); if (middleNmaeMultiValueCF != NULL)CFRelease(middleNmaeMultiValueCF); CFDataRef anAvatarCF = ABPersonCopyImageDataWithFormat(person, kABPersonImageFormatThumbnail); NSData *anAvatarData = (__bridge NSData *)anAvatarCF; UIImage *anAvatar = [UIImage imageWithData:anAvatarData]; if (anAvatarCF != NULL)CFRelease(anAvatarCF); NSDictionary *aPersonDict = [NSDictionary dictionaryWithObjectsAndKeys:name, @"name", [phoneStrArr componentsJoinedByString:@"; "], @"phoneNumber", [emailStrArr componentsJoinedByString:@"; "], @"email", anAvatar, @"avatar", nil]; [contactMutArr addObject:aPersonDict]; NSLog(@"------phoneStrArr :%@",phoneStrArr); NSString *enPhoneNumber = @""; if (phoneStrArr) { enPhoneNumber = [EncryptWithMD5 encryptWithMD5: [phoneStrArr componentsJoinedByString:@"; "]]; } [mStrOfContacts appendString:enPhoneNumber]; [mStrOfContacts appendString:@", "]; if (phoneMultiValue != NULL)CFRelease(phoneMultiValue); if (emailMultiValue != NULL)CFRelease(emailMultiValue); } }else{ for (id aPerson in contactsInAddressBook) { ABRecordRef person = (__bridge ABRecordRef)(aPerson); ABMultiValueRef phoneMultiValue = ABRecordCopyValue(person, kABPersonPhoneProperty); ABMultiValueRef emailMultiValue = ABRecordCopyValue(person, kABPersonEmailProperty); int countEmail = 0; NSMutableArray *emailStrArr; NSMutableArray *phoneStrArr; if (emailMultiValue != NULL) { countEmail = ABMultiValueGetCount(emailMultiValue); } if (countEmail>0) { emailStrArr = [NSMutableArray array]; for (int i = 0; i < countEmail; i++) { CFStringRef anEmailCF = ABMultiValueCopyValueAtIndex(emailMultiValue, i); NSString *anEmail = (__bridge NSString *)anEmailCF; [emailStrArr addObject:anEmail]; if (anEmailCF != NULL)CFRelease(anEmailCF); } } int count = ABMultiValueGetCount(phoneMultiValue); if (count > 0) { phoneStrArr = [NSMutableArray array]; for (int i = 0; i < count; i++) { CFStringRef anPhoneCF = ABMultiValueCopyValueAtIndex(phoneMultiValue, i); NSString *anPhone = (__bridge NSString *)anPhoneCF; NSCharacterSet *cs = [[NSCharacterSet characterSetWithCharactersInString:@"0123456789"] invertedSet]; NSString *anPhonePureNumber = [[anPhone componentsSeparatedByCharactersInSet:cs] componentsJoinedByString:@""]; [phoneStrArr addObject:anPhonePureNumber]; if (anPhoneCF != NULL)CFRelease(anPhoneCF); } } CFStringRef lastNameMultiValueCF = ABRecordCopyValue(person, kABPersonLastNameProperty); CFStringRef firstNmaeMultiValueCF = ABRecordCopyValue(person, kABPersonFirstNameProperty); CFStringRef middleNmaeMultiValueCF = ABRecordCopyValue(person, kABPersonMiddleNameProperty); NSString *lastNameMultiValue = (__bridge NSString *)lastNameMultiValueCF; NSString *firstNmaeMultiValue = (__bridge NSString *)firstNmaeMultiValueCF; NSString *middleNmaeMultiValue = (__bridge NSString *)middleNmaeMultiValueCF; NSString *name = [NSString stringWithFormat:@"%@%@%@", (![firstNmaeMultiValue length])?@"":firstNmaeMultiValue, (![middleNmaeMultiValue length])?@"":middleNmaeMultiValue,(![lastNameMultiValue length])?@"":lastNameMultiValue]; if (lastNameMultiValueCF != NULL)CFRelease(lastNameMultiValueCF); if (firstNmaeMultiValueCF != NULL)CFRelease(firstNmaeMultiValueCF); if (middleNmaeMultiValueCF != NULL)CFRelease(middleNmaeMultiValueCF); CFDataRef anAvatarCF = ABPersonCopyImageDataWithFormat(person, kABPersonImageFormatThumbnail); NSData *anAvatarData = (__bridge NSData *)anAvatarCF; UIImage *anAvatar = [UIImage imageWithData:anAvatarData]; if (anAvatarCF != NULL)CFRelease(anAvatarCF); NSDictionary *aPersonDict = [NSDictionary dictionaryWithObjectsAndKeys:name, @"name", [phoneStrArr componentsJoinedByString:@"; "], @"phoneNumber", [emailStrArr componentsJoinedByString:@"; "], @"email", anAvatar, @"avatar", nil]; [contactMutArr addObject:aPersonDict]; NSString *enPhoneNumber = [EncryptWithMD5 encryptWithMD5: [phoneStrArr componentsJoinedByString:@"; "]]; [mStrOfContacts appendString:enPhoneNumber]; [mStrOfContacts appendString:@", "]; if (phoneMultiValue != NULL)CFRelease(phoneMultiValue); if (emailMultiValue != NULL)CFRelease(emailMultiValue); } } self.contactArr = [[NSArray alloc] initWithArray: contactMutArr]; strOfContacts = [NSString stringWithString:mStrOfContacts]; } 

Editar 2

 -(void)beginFetchContacts{ // Request authorization to Address Book ABAddressBookRef addressBookRef = NULL; if (ABAddressBookRequestAccessWithCompletion) { CFErrorRef *aError=nil; addressBookRef = ABAddressBookCreateWithOptions(NULL, aError); if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined) { ABAddressBookRequestAccessWithCompletion(addressBookRef, ^(bool granted, CFErrorRef error) { // First time access has been granted, add the contact if (granted) { [self dofetchContacts:addressBookRef]; }else{ // [self alertActionSwitchOnTheContactsAccess]; [self buttonCancelPressed:nil]; } }); } else if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized) { // The user has previously given access, add the contact [self dofetchContacts:addressBookRef]; } }else{ addressBookRef = ABAddressBookCreate(); [self dofetchContacts:addressBookRef]; } if (addressBookRef != NULL)CFRelease(addressBookRef); } 

¿Podría ser que está llamando a ABAddressBookCreateWithOptions () y / o ABAddressBookRequestAccessWithCompletion () en un hilo diferente desde donde llama a ABAddressBookCopyArrayOfAllPeopleInSourceWithSortOrdering ()?

Tenga en count lo siguiente en la documentation de la API de Apple:

El controller de finalización se invoca en una queue arbitraria. Si su aplicación utiliza una libreta de direcciones en toda la aplicación, usted es responsable de garantizar que todo el uso de esa libreta de direcciones se envíe a una sola queue para garantizar una operación segura de subprocesss.

Fuente: http://developer.apple.com/library/ios/#documentation/AddressBook/Reference/ABAddressBookRef_iPhoneOS/Reference/reference.html

De forma alternativa, compruebe para asegurarse de que no está liberando prematuramente el ABAddressBookRef que obtuvo de ABAddressBookCreateWithOptions (). Recuerde que ABAddressBookRequestAccessWithCompletion () es asíncrono.

Veo que el hilo bloqueado es "com.apple.root.default-priority"

ABAddressBook NO es seguro para subprocesss, entonces si lo llama desde dos subprocesss diferentes, arroja una exception y bloquea la aplicación .

incluso si siempre envía sus llamadas a DISPATCH_QUEUE_PRIORITY_DEFAULT, puede ejecutarse en dos subprocesss diferentes, porque DISPATCH_QUEUE_PRIORITY_DEFAULT no es una queue de serie.

Use esto para crear su queue de serie que se distribuye a DISPATCH_QUEUE_PRIORITY_DEFAULT:

 dispatch_queue_t abQueue = dispatch_queue_create("myabqueue", DISPATCH_QUEUE_SERIAL); dispatch_set_target_queue(abQueue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)); 

Recuerde enviar (sincronizar o asíncrono) todas sus llamadas a la libreta de direcciones a esta queue.

EDITAR:

Parece que está llamando a dofetchContacts en un bloque de controller de finalización pasado a la function ABAddressBookRequestAccessWithCompletion . ¡Asegúrate de enviar esta llamada en el hilo principal!

La documentation dice:

El controller de finalización se invoca en una queue arbitraria. Si su aplicación utiliza una libreta de direcciones en toda la aplicación, usted es responsable de garantizar que todo el uso de esa libreta de direcciones se envíe a una sola queue para garantizar una operación segura de subprocesss.

 - (IBAction)btn_addprofile:(id)sender { // creating the picker ABPeoplePickerNavigationController *picker = [[ABPeoplePickerNavigationController alloc] init]; // place the delegate of the picker to the controll picker.peoplePickerDelegate = self; // showing the picker app.appstart=0; [self presentModalViewController:picker animated:YES]; // releasing [picker release]; } - (void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker { // assigning control back to the main controller [self dismissModalViewControllerAnimated:YES]; } - (BOOL)peoplePickerNavigationController: (ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person { add_profile_screen *viewcontroller=[[add_profile_screen alloc]initWithNibName:@"add_profile_screen" bundle:nil]; // setting the first name NSString *str_f =(NSString *)ABRecordCopyValue(person, kABPersonFirstNameProperty); NSString *str_l=(NSString *)ABRecordCopyValue(person, kABPersonLastNameProperty); NSLog(@"%@",str_f); NSLog(@"%@",str_l); if([str_f isEqualToString:@""] || [str_l isEqualToString:@""] || !str_f || !str_l) { if([str_f isEqualToString:@""] || !str_f ) { viewcontroller.strfirstname=[NSString stringWithFormat:@"%@",str_l]; } else { viewcontroller.strfirstname=[NSString stringWithFormat:@"%@ ",str_f]; } } else { viewcontroller.strfirstname=[NSString stringWithFormat:@"%@ %@",str_f,str_l]; } // viewcontroller.strname=[NSString stringWithFormat:@"%@",(NSString *)ABRecordCopyValue(person, kABPersonLastNameProperty)]; ABMutableMultiValueRef multi = ABRecordCopyValue(person, kABPersonEmailProperty); if (ABMultiValueGetCount(multi) > 0) { // collect all emails in array // for (CFIndex i = 0; i < ABMultiValueGetCount(multi); i++) for (CFIndex i = 0; i <1; i++) { CFStringRef emailRef = ABMultiValueCopyValueAtIndex(multi, i); viewcontroller.strlastname= (NSString *)emailRef; CFRelease(emailRef); } } // setting the number ABMultiValueRef multi1 = ABRecordCopyValue(person, kABPersonPhoneProperty); viewcontroller.strnumber=[NSString stringWithFormat:@"%@",(NSString*)ABMultiValueCopyValueAtIndex(multi1, 0)]; NSLog(@"%@",viewcontroller.strnumber); [self.navigationController pushViewController:viewcontroller animated:YES]; [viewcontroller release]; // remove the controller [self dismissModalViewControllerAnimated:YES]; return NO; } - (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier { return NO; }