Я пытаюсь выполнить Задание 6 из курса CS193P Пола Хегарти. Одним словом, это iOS-приложение, используемое для просмотра фотографий, загруженных с Flickr. Приложение имеет две вкладки:
- Первая вкладка используется для просмотра фотографий по тегу. Когда вы щелкаете по тегу, отображаются фотографии с этим набором тегов. Когда вы нажимаете на фотографию, она отображается. Все это достигается с помощью табличных представлений, встроенных в контроллер навигации, и, наконец, UIScrollView для отображения.
- Вторая вкладка используется для просмотра недавно просмотренных фотографий в виде таблицы.
Информация о фотографиях и тегах хранится в Core Data. Эти данные отображаются в таблицах через NSFetchedResultsController
.
Вот моя проблема: пока я не обновляю объекты Core Data, все в порядке. Когда я обновляю объект (например, устанавливаю свойство lastViewed для фотографии, чтобы отображать ее на вкладке «Недавние»), соответствующая фотография снова загружается с Flickr при следующем обновлении табличного представления, что приводит к дублированию записи в табличное представление. После долгого сеанса отладки я, наконец, обнаружил проблему, но не могу объяснить, почему: это связано с обновлением объекта Core Data без явного сохранения изменений.
Я прочитал Руководство по программированию основных данных, а также различную документацию по классам, но мне не удалось найти ответ по этому поводу.
Вот код для обновления свойства Photo lastViewed, когда пользователь хочет его отобразить. Если я раскомментирую строку [[SharedDocument sharedInstance] saveDocument]
, все будет работать как положено. Если я его прокомментирую, просмотренное фото будет снова загружено при следующем обновлении, тогда как оно уже существует в Core Data.
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
// - Variable check discarded for a readability purpose -
if ([segue.identifier isEqualToString:@"setImageURL:"])
{
Photo *photo = [self.fetchedResultsController objectAtIndexPath:indexPath];
// Update the last viewed date property to now
photo.lastViewed = [NSDate dateWithTimeIntervalSinceNow:0];
// If I uncomment the line below, the issue disappears:
// [[SharedDocument sharedInstance] saveDocument];
if ([segue.destinationViewController respondsToSelector:@selector(setImageURL:)])
{
// Prepare the next VC
}
}
}
NSManagedObjectContext
является общим. Вот код из общего объекта:
@interface SharedDocument()
@property (strong, nonatomic) UIManagedDocument * document;
@end
@implementation SharedDocument
+ (SharedDocument *) sharedInstance
{...} // Returns the shared instance
- (UIManagedDocument *) document
{...} // Lazy instantiation
- (void) useDocumentWithBlock:(void (^)(BOOL success))completionHandler
{...} // Create or open the document
- (void) saveDocument
{
[self.document saveToURL:self.document.fileURL
forSaveOperation:UIDocumentSaveForOverwriting
completionHandler:nil];
}
Обновления:
Примечание о фотографиях Flickr: набор из 50 фотографий доступен для загрузки с Flickr. Это конечный набор, т.е. новые фотографии добавляться и обновляться не будут. Итак, когда я обновляю табличное представление, новая фотография не должна загружаться.
Объект Photo создается следующим образом (это категория из подкласса NSManagedObject
):
+ (Photo *) photoWithFlickrInfo:(NSDictionary *)photoDictionary
inManagedObjectContext:(NSManagedObjectContext *)context
{
Photo * photo = nil;
// Check whether the photo already exists
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Photo"];
NSString *pred = [NSString stringWithFormat:@"uniqueId = %@",
[photoDictionary[FLICKR_PHOTO_ID] description]];
request.predicate = [NSPredicate predicateWithFormat:pred];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"uniqueId"
ascending:YES];
request.sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSError *error = nil;
NSArray *matches = [context executeFetchRequest:request error:&error];
if (!matches || ([matches count] > 1) || error)
{
// Abnormal
NSLog(@"Error accessing database: %@ (%d matches)", [error description], [matches count]);
}
else if (0 == [matches count])
{
// Create the photo
photo = [NSEntityDescription insertNewObjectForEntityForName:@"Photo"
inManagedObjectContext:context];
photo.uniqueId = photoDictionary[FLICKR_PHOTO_ID];
photo.title = [photoDictionary[FLICKR_PHOTO_TITLE] description];
photo.comment = [[photoDictionary valueForKeyPath:FLICKR_PHOTO_DESCRIPTION] description];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
photo.imageURL = [[FlickrFetcher urlForPhoto:photoDictionary
format:FlickrPhotoFormatOriginal] absoluteString];
}
else
{
// iPhone
photo.imageURL = [[FlickrFetcher urlForPhoto:photoDictionary
format:FlickrPhotoFormatLarge] absoluteString];
}
photo.thumbnailURL = [[FlickrFetcher urlForPhoto:photoDictionary
format:FlickrPhotoFormatSquare] absoluteString];
photo.section = [photo.title substringToIndex:1];
// Update the category / tag
for (NSString * category in [photoDictionary[FLICKR_TAGS] componentsSeparatedByString:@" "])
{
// Ignore a couple of categories
if ([@[@"cs193pspot", @"portrait", @"landscape"] containsObject:[category lowercaseString]])
continue;
Tag *tag = [Tag withName:[category capitalizedString] forPhoto:photo inManagedObjectContext:context];
[photo addTagsObject:tag];
}
NSArray *allTags = [[photo.tags allObjects] sortedArrayUsingComparator:^NSComparisonResult(Tag * t1, Tag * t2) {
return [t1.name compare:t2.name];
}];
photo.tagString = ((Tag *) [allTags objectAtIndex:0]).name;
NSLog(@"PhotoTagString: %@", photo.tagString);
// Update the specific 'All' tag
Tag * allTag = [Tag withName:@"All" forPhoto:photo inManagedObjectContext:context];
[photo addTagsObject:allTag];
NSLog(@"[CORE DATA] Photo created: %@ with %d tags", photo.uniqueId, [photo.tags count]);
}
else
{
// Only one entry
photo = [matches lastObject];
NSLog(@"[CORE DATA] Photo accessed: %@", photo.uniqueId);
}
return photo;
}
Надеюсь, мое объяснение было достаточно ясным. Скажите, если вам нужно больше информации для понимания вопроса (это мой первый пост, я еще юный падаван :-)
Заранее большое спасибо,
Флориан
NSFileManager
. - person Florian   schedule 05.08.2013