【发布时间】:2010-11-14 22:02:12
【问题描述】:
我正在使用 Core Data,我只想获取数据集中的第一条记录,这可能吗?
【问题讨论】:
我正在使用 Core Data,我只想获取数据集中的第一条记录,这可能吗?
【问题讨论】:
您可以在 NSFetchRequest 上使用setFetchLimit: 方法来限制获取的记录数。所以如果你只想要第一条记录:
// Given some existing NSFetchRequest *request and NSManagedObjectContext *context:
[request setFetchLimit:1];
NSError *error;
NSArray *results = [context executeFetchRequest:request error:&error];
请注意,对executeFetchRequest:error: 的调用仍将返回一个NSArray;您仍然需要先从数组中拉出第一个对象,然后才能使用它,即使它是大小为 1 的数组。
另一种效率较低的方法:根据您的商店类型,限制抓取可能会显着提高性能。但是,如果不是这样,或者您不那么担心性能,并且以后可能会使用更多数据,则可以提前将第一个对象从数组中拉出:
// Given some existing result NSArray *results:
NSManagedObject *firstManagedObject = [results firstObject];
如果您确定数组中有一个对象,您甚至可以通过执行以下操作将其放入另一个数组(例如用于 UITableViewController):
// Again, with some NSArray *results:
NSArray *singleObjectResult = [results subarrayWithRange:NSMakeRange(0, 1)];
【讨论】:
-firstObject 并警告-subarrayWithRange: 调用。
当然,这部分取决于您所说的“第一条记录”是什么意思。您可能不仅需要将 fetch 限制设置为 1,还需要对 fetch 中的结果进行排序。
例如,我的数据库中有一堆User 对象,我想找到第一个创建帐户的对象。 (假设我已经有了User 的模型,包括一个名为accountCreatedDate 的属性。)
NSEntityDescription *entity = [NSEntityDescription entityForName:@"User" inManagedObjectContext:managedObjectContext];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"accountCreatedDate" ascending:YES]; // ascending YES = start with earliest date
NSFetchRequest *request = [[NSFetchRequest alloc] init];
request.entity = entity;
request.sortDescriptors = @[ sortDescriptor ];
request.fetchLimit = 1;
NSError *error;
NSArray *fetchResults = [managedObjectContext executeFetchRequest:request error:&error];
User *result = fetchResults.firstObject; // nil on failure; check value of 'error'
如果您在限制获取之前不对结果进行排序,则无法确定“首先”会返回什么结果。
【讨论】:
我使用 Objective-C 类别添加了一个名为 firstInManagedObjectContext: 的 NSManagedObject 类方法。
// NSManagedObject+Additions.h
@interface NSManagedObject (Acani)
+ (NSString *)entityName;
+ (NSEntityDescription *)entityInManagedObjectContext:(NSManagedObjectContext *)context;
+ (NSManagedObject *)firstInManagedObjectContext:(NSManagedObjectContext *)context;
@end
// NSManagedObject+Additions.m
#import "NSManagedObject+Additions.h"
@implementation NSManagedObject (Acani)
+ (NSString *)entityName {
return NSStringFromClass([self class]);
}
+ (NSEntityDescription *)entityInManagedObjectContext:(NSManagedObjectContext *)context {
return [NSEntityDescription entityForName:self.entityName inManagedObjectContext:context];
}
+ (NSManagedObject *)firstInManagedObjectContext:(NSManagedObjectContext *)context {
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:[self entityInManagedObjectContext:context]];
[fetchRequest setFetchLimit:1];
NSError *error;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
[fetchRequest release];
if (fetchedObjects == nil) {
NSLog(@"Fetch sender error %@, %@", error, [error userInfo]);
return nil;
} else if ([fetchedObjects count] > 0) {
return [fetchedObjects objectAtIndex:0];
}
return nil;
}
@end
将这些文件添加到您的项目中,并确保将它们链接到您的目标。然后,#import "NSManagedObject+Additions.h" 在您使用firstInManagedObjectContext: 类方法的.m 文件中。
从NSManagedObjectContext 的任何具体(非抽象)子类调用它。只需将 NSManagedObjectContext *context 传递给它即可从中获取托管对象。该方法从调用它的类的名称中检测(NSString *)entityName。请记住将结果转换为干净的构建(没有警告)。
我将它用于我知道我只有一个已保存实例的托管对象类。如果您有多个已保存的实例,您可能需要按照@Craig McMahon 的建议添加NSSortDescriptor。如果所有对象都是在同一设备上创建的,您也可以尝试按objectID 而不是accountCreatedDate 进行排序。但是,我不确定对象 ID 是否已排序。
假设您有一个从NSManagedObjectContext 继承的Event 对象。你可以这样做:
Event *event = (Event *)[Event firstInManagedObjectContext:context];
阅读Pragmatic Studio's iOS 4 Blocks Tutorial 之后,我意识到可以通过添加另一个函数来改进这段代码并使其更加模块化,称之为+ fetch 并添加NSFetchRequestBlock(它将以fetchRequest 作为其参数) & NSFetchRequestFailureBlock 作为参数分别自定义获取请求和获取请求错误的处理。我挑战你解决这个问题! :)
【讨论】:
只需将NSFetchRequest 的fetchLimit 属性设置为1,然后使用firstObject 方法从结果NSArray 中提取第一个元素,以避免任何index out of bound for empty array 错误。
这是代码示例:
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"ENTITY_NAME" inManagedObjectContext:yourMoc];
NSPedicate *predicate = [NSPredicate predicateWithFormat:@"PREDICATE FORMAT"];
[request setEntity:entity];
[request setPredicate:predicate];
[request setFetchLimit:1];
NSError *error;
NSArray *results = [managedObjectContext executeFetchRequest:request error:&error];
NSManagedObject *object = [results firstObject];
if (object) {
// there is at least one object matching you predicate
} else {
// there is no object matching your predicate in the moc
}
【讨论】:
这是我的尝试。 注: Book Entity 继承自 NSManagedObject。
NSPredicate *predicate = [NSPredicate predicateWithFormat: @"isbn ==9780786660506"];
Book *book = (Book*) [CoreDataHelper getEntityFromFetchedResultsController:@"Book" :predicate :nil :YES :application.managedObjectContext];
if (book !=nil) NSLog(@"book entity %@",book.title);
将其放入一个名为 CoreDataHelper 的静态帮助器类中
+ (NSManagedObject*)getEntityFromFetchedResultsController: (NSString*) entityName : (NSPredicate *) predicate : (NSString*) sortKey : (BOOL) sortAscending : (NSManagedObjectContext *) managedObjectContext
{
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:managedObjectContext];
[request setEntity:entity];
[request setFetchLimit:1];
// If a predicate was passed, pass it to the query
if(predicate != nil)
{
[request setPredicate:predicate];
}
// If a sort key was passed, use it for sorting.
if(sortKey != nil)
{
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:sortKey ascending:sortAscending];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
[sortDescriptors release];
[sortDescriptor release];
}
NSError *error;
NSMutableArray *mutableFetchResults = [[[managedObjectContext executeFetchRequest:request error:&error] mutableCopy] autorelease];
if (error !=nil){
NSLog(@"error %@", error);
}
if ([mutableFetchResults count] >0)
{
NSManagedObject *firstObject = [mutableFetchResults objectAtIndex:0];
return firstObject;
}else
{
return nil;
}
}
【讨论】: