Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EXC_BAD_ACCESS with sortUsingComparator

I understand what the error EXC_BAD_ACCESS means in general, but I'm puzzled about what's happening in my case.

I have a custom class which has an NSComparator property sortWithThisComparator. If that property is set by the user, when I insert an item into the instance's class property array items I use the comparator to determine the insertion location:

- (void) insertItem:(id<NSCoding, CKArchivingItem>) item {
    if (arrayObjectClassString && ![item isKindOfClass:NSClassFromString(arrayObjectClassString)]) {
        [NSException raise:@"YOU MADE A MISTAKE" format:@"you tried to insert a %@ but I can only accept %@", [item class], arrayObjectClassString];
    } else {
        if (!sortWithThisComparator) {
            [self.items addObject:item];
        } else {
            NSInteger newItemIndex = [self.items indexOfObject:item inSortedRange:NSMakeRange(0, [self.items count]) options:NSBinarySearchingFirstEqual usingComparator:sortWithThisComparator];
            if (newItemIndex >= [self.items count]) {
                [self.items addObject:items];
            } else {
                [self.items insertObject:item atIndex:newItemIndex];
            }
        }
    }
}

Everything works fine when I don't set a comparator, but when I do use the comparator I get a bad access error:

typedef NSComparisonResult (^NSComparator)(id obj1, id obj2);

...
   [[CKGenericSingletonSubclass sharedManager] setSortWithThisComparator: ^NSComparisonResult(CKGeneralizedItemSubclass *i1, CKGeneralizedItemSubclass *i2) {
        NSLog(@"we are inside");
        NSLog(@"here is the item 1 %@", i1);
        NSLog(@"we are comparing this float %f to thisf loat %f", i1.gradeSchoolAverage, i2.gradeSchoolAverage);
        if (i1.gradeSchoolAverage < i2.gradeSchoolAverage) {
            return NSOrderedAscending;
        } else if (i1.gradeSchoolAverage == i2.gradeSchoolAverage) {
            return NSOrderedSame;
        } else {
            return NSOrderedDescending;
        }
    }];

I get a bad access thread on the second line of the comparator, which is simply logging one of the parameters passed via the NSComparator. Yet the class instances I am passing to the insertItem are accessible elsewhere without this problem, so I know they have been properly instantiated and are otherwise properly passed since I can insert them into the items property fine without a comparator. What am I missing here?


Further details. I am storing the NSComparator as

@property (strong, atomic) NSComparator sortWithThisComparator;
like image 455
sunny Avatar asked Feb 03 '16 12:02

sunny


1 Answers

Actually, I only can make some assumptions according to the codes you mentioned above.

For the first block, I guess self.items is a mutable array. Also in the function insertItem:, once sortWithThisComparator is not nil, new object will be inserted after a sorting process gets an index.

Because you get a bad access on thread issues, I guess you didn't take care of threading very well in your codes.

Here are some suggestion:

  • NSMutableArray is not thread safe: I guess you put self.items operation in an asynchronous thread. If I'm right about this, remember each time you would like to edit self.items, have NSLock to protect it.

Ex:

- (void)addItem:(id)item {
    dispatch_async(self.queue, ^{
        [threadLock lock];
        [self.items addObject:item];
        [threadLock unlock];
    }
}
  • Another one is sorting based on mutable array: I think it's dangerous too since mutable array is not thread safe.

Let's take a look of this line:

NSInteger newItemIndex = [self.items indexOfObject:item inSortedRange:NSMakeRange(0, [self.items count]) options:NSBinarySearchingFirstEqual usingComparator:sortWithThisComparator];

In my opinion, I would do an array copy first and set options with NSBinarySearchingInsertionIndex:

NSArray *array = [self.items copy];
NSInteger newItemIndex = [array indexOfObject:item inSortedRange:NSMakeRange(0, [array count]) options:NSBinarySearchingInsertionIndex usingComparator:sortWithThisComparator];
  • Besides, I found a situation that self.items.count could be Zero and you are making a NSMakeRange(0, self.items.count). Probably also revise the condition little bit in function insertItem:,
if (!sortWithThisComparator || self.items.count == 0) {}

These are some thoughts I have at this movement. I'll add more once I get ideas for other possibilities.

like image 106
Allen Avatar answered Sep 23 '22 16:09

Allen