Я использую Core Data и хочу получить только первую запись в своем наборе данных, возможно ли это?
Как получить только первую запись в Core Data?
Ответы (5)
Вы можете использовать метод setFetchLimit:
в NSFetchRequest, чтобы ограничить количество извлекаемых записей. Итак, если вам нужна только первая запись:
// 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:
.
- person Tim; 10.08.2015
Конечно, это частично зависит от того, что вы подразумеваете под «первой записью». Возможно, вам потребуется не только установить лимит выборки на 1, но и отсортировать результаты в выборке.
Например, у меня в базе есть куча 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, чтобы добавить метод класса NSManagedObject
с именем firstInManagedObjectContext:
.
Исходный код
// 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"
в файлах .m
, где вы используете метод класса firstInManagedObjectContext:
.
Вызовите его из любого конкретного (не абстрактного) подкласса NSManagedObjectContext
. Просто передайте ему NSManagedObjectContext *context
, из которого можно получить управляемый объект. Метод обнаруживает (NSString *)entityName
в имени класса, для которого он вызывается. Только не забудьте привести результат для чистой сборки (без предупреждений).
Я использую это для класса управляемых объектов, о котором я знаю, что у меня есть только один сохраненный экземпляр. Если у вас есть более одного сохраненного экземпляра, вы можете добавить NSSortDescriptor
, как предлагает @Craig McMahon. Вы также можете попробовать отсортировать по objectID
вместо accountCreatedDate
, если все объекты созданы на одном устройстве. Однако я не уверен, упорядочены ли идентификаторы объектов.
Пример
Представьте, что у вас есть объект Event
, который происходит от NSManagedObjectContext
. Вы можете сделать:
Event *event = (Event *)[Event firstInManagedObjectContext:context];
Блоки
Прочитав руководство Pragmatic Studio по блокам iOS 4, я понял, что этот код можно улучшить и сделать еще более модульным, добавив еще одну функцию, назовите ее + fetch
и добавьте NSFetchRequestBlock
(которая будет принимать fetchRequest
в качестве аргумента) и NSFetchRequestFailureBlock
в качестве аргументов для настройки запроса на выборку и обработки ошибки запроса на выборку, соответственно. Я призываю вас справиться с этим! :)
Просто установите для свойства fetchLimit
вашего NSFetchRequest
значение 1, затем выберите первый элемент из результатов NSArray
, используя метод firstObject
, чтобы избежать ошибки 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 наследуется от 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;
}
}