title: Custom sorting with NSFetchedResultController (subclassing NSSortDescriptor)
body: <p>I want to provide custom sorting using NSFetchedResultsController and NSSortDescriptor.</p>
  
  <p>As custom sorting via NSSortDescriptor message -(id)initWithKey:ascending:selector: is not possible (see <a href="https://stackoverflow.com/questions/2168141/core-data-table-view-section-sort-by-weekdays-using-nssortdescriptor">here</a>), I tried to use a NSSortDescriptor derived class in order to override the compareObject:toObject: message.</p>
  
  <p>My problem is that the compareObject:toObject: is not always called. It seems that it is called only when the data are already in memory. There is an optimization of some sort that use a database based sort instead of the compareObject:toObject when the data are retrieved from the store the first time. (see <a href="http://lists.apple.com/archives/cocoa-dev/2009/Aug/msg00960.html" rel="nofollow noreferrer">here</a>).</p>
  
  <p>My question is : how to force NSFetchedResultscontroller to use the compareObject:toObject: message to sort the data ?  (and will it work with large data set)</p>
  
  <p>One solution is to use a binary store instead of a sqlite store but I don't want to do that.</p>
  
  <p>Another solution is:<br>
  -call performFetch to sort data via SQL (compareObject not called)<br>
  -make a modification to the data and reverse it.<br>
  -call performFetch again (compareObject is called)<br>
  It does work in my case but it's a hack and I am not sure it will always work (especially with large data set (greater than the batch size)).</p>
  
  <p><strong>UPDATED:You can reproduce with the CoreDataBooks sample.</strong><br>
  In RootViewController.m, add this ugly hack:  </p>
  
  <pre><code>- (void)viewWillAppear:(BOOL)animated {
      Book* book = (Book *)[NSEntityDescription insertNewObjectForEntityForName:@"Book" 
                   inManagedObjectContext:[self fetchedResultsController].managedObjectContext];
      [[self fetchedResultsController] performFetch:nil];
      [[self fetchedResultsController].managedObjectContext deleteObject:book];
      [self.tableView reloadData];
  }
  </code></pre>
  
  <p>In RootViewController.m, replace the sort descriptor code with:  </p>
  
  <pre><code>MySortDescriptor *myDescriptor = [[MySortDescriptor alloc] init];
  NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:myDescriptor, nil];
  [fetchRequest setSortDescriptors:sortDescriptors];  
  </code></pre>
  
  <p>Add MySortDescriptor class:  </p>
  
  <pre><code>@implementation MySortDescriptor
  
  -(id)init
  {
      if (self = [super initWithKey:@"title" ascending:YES selector:@selector(compare:)])
      {
  
      }
      return self;
  }
  
  - (NSComparisonResult)compareObject:(id)object1 toObject:(id)object2
  {
      //set a breakpoint here
      return [[object1 valueForKey:@"author" ] localizedCaseInsensitiveCompare:[object2 valueForKey:@"author" ] ];
  }
  
  //various overrides inspired by [this blog post][3]
  - (id)copy
  {
      return [self copyWithZone:nil ];
  }
  - (id)mutableCopy
  {
      return [self copyWithZone:nil ];
  }
  - (id)mutableCopyWithZone:(NSZone *)zone
  {
      return [self copyWithZone:zone ];
  }
  - (id)copyWithZone:(NSZone*)zone
  {
      return [[MySortDescriptor alloc] initWithKey:[self key] ascending:[self ascending] selector:[self selector]];
  }
  - (id)reversedSortDescriptor
  {
      return [[[MySortDescriptor alloc] initWithKey:[self key] ascending:![self ascending] selector:[self selector]] autorelease];
  }
  @end
  </code></pre>