Reputation: 31
I made a bug when I implement the search function. I opened an asynchronous thread. But when deleting a character (a digit of a phone number), the app would crash.
Error:
*** Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <__NSArrayM: 0x117d7320> was mutated while being enumerated.'
Code:
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText{
//
self.isSearch = YES;
//remove the last search all the contacts
[self.resultArr removeAllObjects];
//
[self.rcs_SearchTableView reloadData];
//
dispatch_queue_t uploadQueue = dispatch_get_global_queue(0, 0);
dispatch_queue_t getMainQueue = dispatch_get_main_queue();
dispatch_async(uploadQueue, ^{
NSMutableArray *phoneArr = (NSMutableArray *)[self rcs_GetPhoneNumberFromeDatabaseWithPhone:searchText];
//
//NSLog(@"清空上次搜索的数据:%@", self.resultArr);
//NSLog(@"输入的关键字是---%@---%lu",searchText,(unsigned long)searchText.length);
if (0 == searchText.length || [searchText isEqualToString:@" "]) {
self.isSearch = NO;
//[self.rcs_SearchTableView reloadData];
[self.resultArr removeAllObjects];
}
//[self.rcs_SearchTableView reloadData];
if (0 != phoneArr.count) {
//
for (NSUInteger i = 0; i < phoneArr.count; i ++) {
RCSPhoneModel *flagPhoneModel = phoneArr[i];
for (NSUInteger i = 0; i < self.rcsRecentSearchDataSource.count; i ++) {
RCSContactModel *flagModel = self.rcsRecentSearchDataSource[i];
if ([flagPhoneModel.serverId isEqualToString:flagModel.serverId] || [flagPhoneModel.phone isEqualToString:flagModel.name]) {
//the same contact has multiple Numbers To prevent repeated add the same contacts
if (![self.resultArr containsObject:flagModel]) {
[self.resultArr addObject:flagModel];
continue;
}
}
}
}
}else{
//search contacts by name
for (NSInteger i = 0; i < self.rcsRecentSearchDataSource.count; i ++) {
RCSContactModel *model = self.rcsRecentSearchDataSource[i];
NSString *nameStr = model.name;
if (nameStr.length >= searchText.length) {
//search all the name
if ([nameStr containsString:searchText]) {
[self.resultArr addObject:model];
}
}
}
}
//
if (self.resultArr.count > 0) {
self.isSearch = YES;
//[self.rcs_SearchTableView reloadData];
}
//The phone contacts or local contact synchronized to the server
dispatch_async(getMainQueue, ^{
[self.rcs_SearchTableView reloadData];
});
});
}
Upvotes: 2
Views: 2188
Reputation: 31
I got it answer and like unders codes: - (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText{ // self.isSearch = YES; // if (0 == searchText.length || [searchText isEqualToString:@" "]) { self.isSearch = NO; //[self.resultArr removeAllObjects]; } //Remove the last search all the contacts [self.resultArr removeAllObjects]; // [self.rcs_SearchTableView reloadData]; // NSMutableArray *localSearchDataSource = [self.resultArr mutableCopy]; //Create an array of objects as well as the original array //According to the input access to the phone number of the data NSArray *localPhoneArr = [[self rcs_GetPhoneNumberFromeDatabaseWithPhone:searchText] copy]; // //[self.rcs_SearchTableView reloadData]; if (0 != localPhoneArr.count) { //Because when data matching number takes longer, using asynchronous thread dispatch_queue_t uploadQueue = dispatch_get_global_queue(0, 0); dispatch_queue_t getMainQueue = dispatch_get_main_queue(); dispatch_async(uploadQueue, ^{ // for (NSUInteger i = 0; i < localPhoneArr.count; i ++) { RCSPhoneModel *flagPhoneModel = localPhoneArr[i]; for (NSUInteger i = 0; i < self.rcsRecentSearchDataSource.count; i ++) { RCSContactModel *flagModel = self.rcsRecentSearchDataSource[i];
if ([flagPhoneModel.serverId isEqualToString:flagModel.serverId] || [flagPhoneModel.phone isEqualToString:flagModel.name]) {
//The same contact has multiple Numbers To prevent repeated add the same contacts
if (![localSearchDataSource containsObject:flagModel]) {
[localSearchDataSource addObject:flagModel];
}
}
}
}
//Add the search results to the search data source
dispatch_async(getMainQueue, ^{
[self.resultArr addObjectsFromArray:localSearchDataSource];
[self.rcs_SearchTableView reloadData];
});
});
}else{
//Search contacts by name
for (NSInteger i = 0; i < self.rcsRecentSearchDataSource.count; i ++) {
RCSContactModel *model = self.rcsRecentSearchDataSource[i];
NSString *nameStr = model.name;
if (nameStr.length >= searchText.length) {
//Search all name
if ([nameStr containsString:searchText]) {
[self.resultArr addObject:model];
}
}
}
}
//
if (self.resultArr.count > 0) {
self.isSearch = YES;
[self.rcs_SearchTableView reloadData];
}
}
I made two change that 'NSMutableArray *localSearchDataSource = [self.resultArr mutableCopy];' and 'NSArray *localPhoneArr = [[self rcs_GetPhoneNumberFromeDatabaseWithPhone:searchText] copy];'. And finish it . Collection <__NSArrayM: 0x117d7320> was mutated while being enumerated.'
Upvotes: 0
Reputation: 1779
A for
loop should not enumerate anything that could change on any other thread or that could change within that loop. You should only enumerate an object that you are certain is not going to change while being enumerated (either in another thread, or within the loop itself). One way to do this is to only use a local copy of the array to enumerate over.
I can't see where anything being enumerated in your for
loops is changed within the loop, so I would guess that in some other code in some other thread, you are changing either self.rcsRecentSearchDataSource
or phoneArr
. This crashes the for
loop that enumerates self.rcsRecentSearchDataSource
or phoneArr
because it is required to not change while being enumerated.
Does this really need to be run on a separate thread?
If so, use a thread-local copy of the array to enumerate over, instead of the original array. That way you can be sure that nothing else can modify it, because it does not exist in any other scope.
Eg, there are two places where you could change your code to:
NSArray *localSearchDataSource = [self.rcsRecentSearchDataSource copy];
for (NSUInteger i = 0; i < localSearchDataSource.count; i ++) {
and one place where you could change to:
NSArray *localPhoneArr = [[self rcs_GetPhoneNumberFromeDatabaseWithPhone:searchText] copy];
for (NSUInteger i = 0; i < localPhoneArr .count; i ++) {
Upvotes: 1