使用NSFetchedResultController进行自定义排序(子类化NSSortDescriptor) [英] Custom sorting with NSFetchedResultController (subclassing NSSortDescriptor)

查看:149
本文介绍了使用NSFetchedResultController进行自定义排序(子类化NSSortDescriptor)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想使用NSFetchedResultsController和NSSortDescriptor提供自定义排序。



通过NSSortDescriptor消息进行自定义排序 - (id)initWithKey:ascending:selector: 这里),我试图使用NSSortDescriptor派生类为了覆盖compareObject:toObject:message。



我的问题是compareObject:toObject:不总是被调用。它似乎只有当数据已经在内存中时才被调用。当第一次从商店检索数据时,存在使用基于数据库的排序而不是compareObject:toObject的某种排序的优化。 (请参阅此处)。



我的问题是:如何强制NSFetchedResultscontroller使用compareObject:toObject:message来排序数据? (并且它将与大数据集合工作)



一个解决方案是使用二进制存储而不是sqlite存储,但我不想这样做。另一个解决方案是:

-call performFetch通过SQL对数据进行排序(compareObject未被调用)

- 修改数据并反转。

-call再次执行(比较对象被调用)

它在我的情况下工作,但它是一个黑客,我不确定它会一直工作(特别是大数据集(大于批处理大小))。



UPDATED:您可以使用CoreDataBooks示例进行再现。

在RootViewController.m中,添加这个丑陋的黑客:

   - (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];
}

在RootViewController.m中,将排序描述符代码替换为:

  MySortDescriptor * myDescriptor = [[MySortDescriptor alloc] init]; 
NSArray * sortDescriptors = [[NSArray alloc] initWithObjects:myDescriptor,nil];
[fetchRequest setSortDescriptors:sortDescriptors];

添加MySortDescriptor类:

  @implementation MySortDescriptor 

- (id)init
{
if(self = [super initWithKey:@titleascending:YES selector:@ selector(compare :)])
{

}
return self;
}

- (NSComparisonResult)compareObject:(id)object1 toObject:(id)object2
{
//在这里设置一个断点
return [ [object1 valueForKey:@author] localizedCaseInsensitiveCompare:[object2 valueForKey:@author]];
}

//各种覆盖由[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] self selector]];
}
- (id)reversedSortDescriptor
{
return [[[MySortDescriptor alloc] initWithKey:[self key] ascending:![self ascending] selector:[self selector] autorelease];
}
@end


解决方案

参考你的问题和意见。你将需要将对象拉到内存中进行排序。一旦他们在记忆中,你可以使用一个方便的方法来确定一个点的距离。



要减少存入内存的对象数量,您可以计算最大值和最小值,然后对这些值进行过滤,在排序之前减少搜索的半径。 p>

不能对计算的值进行排序,除非它在内存中。


I want to provide custom sorting using NSFetchedResultsController and NSSortDescriptor.

As custom sorting via NSSortDescriptor message -(id)initWithKey:ascending:selector: is not possible (see here), I tried to use a NSSortDescriptor derived class in order to override the compareObject:toObject: message.

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 here).

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)

One solution is to use a binary store instead of a sqlite store but I don't want to do that.

Another solution is:
-call performFetch to sort data via SQL (compareObject not called)
-make a modification to the data and reverse it.
-call performFetch again (compareObject is called)
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)).

UPDATED:You can reproduce with the CoreDataBooks sample.
In RootViewController.m, add this ugly hack:

- (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];
}

In RootViewController.m, replace the sort descriptor code with:

MySortDescriptor *myDescriptor = [[MySortDescriptor alloc] init];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:myDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];  

Add MySortDescriptor class:

@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

解决方案

In reference to your question and the comments. You are going to need to pull the objects into memory to sort them. Once they are in memory you can use a convenience method to determine distance from a point.

To decrease the number of objects you pull into memory you could calculate max and min values and then filter on those, reducing the radius of your search before you sort.

It is not possible to sort on a calculated value unless it is in memory.

这篇关于使用NSFetchedResultController进行自定义排序(子类化NSSortDescriptor)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆