Search code examples
iosnsarraynspredicate

Filter huge NSArray


I'm filtering an NSArray with a NSPredicate and using the filtered array for my UITableView. I'm using this filtering when the user inputs text in a UITextField. So every time the text in the UITextField changes I'm calling my filter function.

It looks like this:

NSArray *hugeArray = ...;
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name == %@", input];
_resultArray = [hugeArray filteredArrayUsingPredicate:predicate];
[_myTableView reloadData];

When I'm using a NSArray with a lot of objects the input becomes very slow (the complete input in the UI becomes slow). Is there any possibility to get a better performance or run the filtered command in background?

Writing something in the UITextField shouldn't be blocked. When the UITableView will be refreshed after a very short time after the input it may be ok.


Solution

  • NSPredicate focuses on flexibility rather than speed. For an in-memory NSArray (i.e. not a Core Data relationship), you can generally get much better performance by just using a loop.

    If it is still too slow, then there are several approaches:

    • Coalesce your requests. See Is there a simple way (in Cocoa/iOS) to queue a method call to run once in the next run loop? You can create a coalescing trampoline so that you only will update your list every few hundred milliseconds. That way if the user types very quickly, you don't re-filter the list every single chaacter.

    • Be smarter about filtering. If you just filtered for "bo" and you now want to filter for "bob", you know it's a subset of that previous list. You don't have to re-filter everything. Writing a good algorithm for this takes a little work but can dramatically improve performance.

    • Perform your filtering on an NSOperationQueue (easier to cancel than GCD, but GCD also works), and let the UI use KVO to notice when the filtered array changes.

    • Keep track of the actual changes (adds/deletes) while filtering. You shouldn't call reloadData on your table view if you can help it. You should perform inserts and deletes (insertRowsAtIndexPaths:). That avoids constantly churning your cells, and it also generally looks better. Again, the code is more complicated, but the improvements can be dramatic.