Как использовать NSCache с CoreData

Я разработал проект, в котором пользователь рисует изображение на холсте, я сохраняю его в файле с помощью CoreData, у меня есть отношение «один ко многим», называемое «папка-к-файлам». Итак, здесь все изображения. Я извлекаю изображения из файлов, изменяю размер в соответствии с высотой ячейки таблицы и показываю их на столе. Как только это показано, я хочу кэшировать изображения.

У меня также есть несколько меток в ячейке папки, которые дают мне некоторую информацию о моих файлах, которые я обновляю на лету. Я также прокручиваю ячейки, чтобы пометить их как завершенные, и перемещаю их в нижнюю часть ячейки.

Я также показываю одни и те же изображения файлов в разных представлениях в зависимости от того, как пользователь их запрашивает.

Я хочу знать лучший метод для этого, я читал в Интернете, их много методов, GCD, NSOperationQueues и многое другое.

Какой способ мне больше подойдет.

Я хочу показать код

- (UITableViewCell *)tableView:(FMMoveTableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";

    FolderCell *tableCell = (FolderCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    if (tableCell == nil)
    {
        NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"FolderCell" owner:self options:nil];
        tableCell = [nib objectAtIndex:0];
    }

    NSMutableArray *categoryArray = [[self.controller fetchedObjects]mutableCopy];

    Folder *category = [categoryArray objectAtIndex:[indexPath row]];

    [tableCell configureCellWithNote:category]; //This function is written in my FolderCell.m function        

}
    return tableCell; 
}


-(void)configureCellWithNote:(Folder *)category
{    
    self.category = category;


    UIImage *image1 = [UIImage imageWithData:category.noteImage];
    CGSize newSize;


    if(image1.size.width == 620 && image1.size.height == 200)
   {              
       newSize = CGSizeMake(300, 97);
   }     

    UIGraphicsBeginImageContextWithOptions(newSize, NO, 0.0);

    [image1 drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];


    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();     

    self.notesImage.image = newImage;    
 }

Итак, здесь происходит то, что configureCellWithNote занимает много времени, потому что изменяет размер изображений. Пожалуйста, помогите мне решить, как решить эту проблему с производительностью.

С уважением Раджит


person Ranjit    schedule 11.12.2013    source источник
comment
Я думаю, что ipmcc ответил на вопрос о кеше. Кстати, я мог бы также поместить эту логику изменения размера изображения в категорию UIImage. Кроме того, что, если бы изображение было не 620x200, это не работало бы должным образом. Безопаснее использовать общий алгоритм изменения размера, для которого вы можете использовать UIViewContentModeScaleAspectFill или UIViewContentModeScaleAspectFit, например. stackoverflow.com/questions/10491080/< /а>   -  person Rob    schedule 11.12.2013


Ответы (1)


Если вы просто хотите перенести операцию изменения размера в фоновый поток, вы можете сделать что-то вроде этого:

- (void)configureCellWithNote:(Folder *)category
{
    self.category = category;

    UIImage *image1 = [UIImage imageWithData:category.noteImage];
    CGSize newSize;

    if(image1.size.width == 620 && image1.size.height == 200)
    {
        newSize = CGSizeMake(300, 97);
    }

    dispatch_async(dispatch_get_global_queue(0,0), ^{
        UIGraphicsBeginImageContextWithOptions(newSize, NO, 0.0);

        [image1 drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];

        UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();

        UIGraphicsEndImageContext();

        dispatch_async(dispatch_get_main_queue(), ^{
            self.notesImage.image = newImage;
        });
    });

}

Если вы хотите кэшировать результаты, то хитрость будет заключаться в том, чтобы придумать хороший ключ кэша. К сожалению, из того, что вы опубликовали, трудно сказать, что могло бы стать хорошим ключом кеша. Конечно, он должен включать размер, но он также должен включать что-то, что связывает его с категорией. Я полагаю, что вы могли бы использовать NSManagedObjectID для категории, но я думаю, что это будет специфично для каждого контекста управляемого объекта, который у вас есть. Если предположить, что в Folder есть свойство uniqueName, реализация кэширования может выглядеть так:

- (UIImage*)imageForCategory: (Folder*)category atSize: (CGSize)size
{
    // A shared (i.e. global, but scoped to this function) cache
    static NSCache* imageCache = nil;
    // The following initializes the cache once, and only once
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        imageCache = [[NSCache alloc] init];
    });

    // Generate a cache key sufficient to uniquely identify the image we're looking for
    NSString* cacheKey = [NSString stringWithFormat: @"%@|%@", category.uniqueName, NSStringFromSize((NSSize)size)];
    // Try fetching any existing image for that key from the cache.
    UIImage* img = [imageCache objectForKey: cacheKey];

    // If we don't find a pre-existing one, create one
    if (!img)
    {
        // Your original code for creating a resized image...
        UIGraphicsBeginImageContextWithOptions(size, NO, 0.0);
        UIImage* image1 = [UIImage imageWithData:category.noteImage];
        [image1 drawInRect:CGRectMake(0,0,size.width,size.height)];
        img = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();

        // Now add the newly-created image to the cache 
        [imageCache setObject: img forKey: cacheKey];
    }

    // Return the image 
    return img;
}
person ipmcc    schedule 11.12.2013
comment
Привет, @ipmcc, спасибо за ответ, у меня есть атрибут с именем папки. Итак, код, который вы показали, должен ли я написать его в tableCell.m или в моей функции ViewController.m CellForRowAtIndexPath. И только ли этот код будет обрабатывать кэширование изображений? Извините, что спрашиваю об этом, но я новичок в этом. Не могли бы вы объяснить код, который вы написали. - person Ranjit; 11.12.2013
comment
Вероятно, имеет смысл использовать его в классе ViewController, а не в классе TableCell. Этот код должен обрабатывать кэширование изображений. Если кеш не содержит изображения для определенной папки и размера, он сгенерирует его. - person ipmcc; 11.12.2013
comment
+1 На отличный ответ. Кстати, учитывая, что NSCache изменил свое поведение с iOS 7, вы можете создать подкласс NSCache, чтобы гарантировать, что вы реагируете на нехватку памяти. См. раздел stackoverflow.com/questions/19546054/. Что касается кэширования в контроллере представления или классе ячеек, если есть вероятность, что вам могут понадобиться эти измененные изображения в другом месте, вы, вероятно, не хотите их ни в одном из этих классов, а скорее в каком-то контроллере модели или синглтоне, который делает его доступным для других просмотреть контроллеры. - person Rob; 11.12.2013
comment
@ipmcc, Роб, я хочу это в классе ячеек, потому что я делаю здесь все операции, так почему я должен писать это в viewController - person Ranjit; 11.12.2013
comment
Я полагаю, вы могли бы сделать это методом класса вашего класса ячейки. Практически говоря, не имеет значения, где вы его поместите. Говоря философски, вы должны поставить его в самое конкретное место, где все вещи, которые должны его вызывать, могут до него добраться. Если он является общим для всех ячеек, но больше нигде не используется, то метод класса для класса tableCell имеет смысл. - person ipmcc; 11.12.2013
comment
@ Ранджит Все в порядке. Но по мере того, как приложение растет и вы начинаете использовать миниатюры повсюду, вам может не понадобиться отдельный кеш для миниатюрных изображений для каждого класса ячеек (или для каждого контроллера представления). Это особенно верно, если вы можете показывать определенную миниатюру где-то еще, кроме этого конкретного представления таблицы. Я лично поддерживаю кеши для различных функциональных категорий (например, эскизы, полноразмерные изображения и т. д.), а не для конкретных отдельных классов пользовательского интерфейса. Но вы можете поместить свой кеш (кеши) везде, где это имеет смысл для вашей конкретной архитектуры приложения. - person Rob; 11.12.2013
comment
Хорошо, спасибо вам обоим. Еще одна вещь. Может ли кто-нибудь кратко объяснить мне этот фрагмент кода? - person Ranjit; 11.12.2013
comment
Я просмотрел и добавил комментарии к коду, будучи настолько явным, насколько это возможно. Если вам нужно больше объяснений, чем это, вам нужно будет более конкретно указать, что неясно. - person ipmcc; 11.12.2013
comment
@Rob, я использовал процесс кэширования, как объяснил ipmcc, но если я не запущу метод drawRect в фоновом потоке, мое приложение будет работать медленно. Но я не понимаю, как только изображение будет кэшировано, приложение не войдет. Метод DrawRect, но все же профилировщик времени показывает, что он потребляет 60% времени. - person Ranjit; 12.12.2013
comment
@Ranjit Вы, кажется, думаете, что я не согласен с идеей рендеринга изображения в фоновом режиме. Наоборот, я думаю, что вы должны выполнять этот дорогостоящий процесс в фоновой очереди. Я только придирался к предположению, что класс ячейки должен владеть кешем. - person Rob; 12.12.2013
comment
@Rob, Ничего подобного, я просто хотел развеять свои сомнения. Я немного боюсь использовать многопоточность, потому что мой пользовательский интерфейс не загружается должным образом. Но в любом случае я разберусь с этим. Спасибо за ваш ценный ответ. Я буду использовать подход фонового потока - person Ranjit; 12.12.2013
comment
@ipmcc, я работаю над приложением для iPad, в котором есть вкладки. Итак, вот тот случай, если я нахожусь на другой вкладке, называемой настройками, и нажимаю кнопку «Домой», затем я снова открываю приложение, оно выводит меня на страница настроек, теперь, когда я переключаю вкладку, содержащую изображения, происходит то, что она не загружает изображения из кеша, потому что она получает нулевые изображения, и поэтому она снова создает их, из-за чего я могу видеть сначала пустая таблица, а затем на нее загружаются изображения. Это потому, что кеш инициализируется только один раз? Должны ли мы инициализировать кеш еще раз. - person Ranjit; 13.12.2013
comment
Я не могу знать, не видя его в действии, но одна из основных особенностей NSCache заключается в том, что ОС может в любой момент удалить данные из кэша. Если вам нужна надежная гарантия того, что вещи, которые вы поместили в кеш, все еще будут там, NSCache не подходит. - person ipmcc; 13.12.2013
comment
@Rob, вы предложили мне создать подкласс NSCache, потому что он не вытесняет объекты из-за предупреждения о памяти. Я заметил еще один случай, когда приложение переходит в фоновый режим, кэш становится пустым. Так ли это? - person Ranjit; 16.12.2013
comment
Да, похоже, это вещь: stackoverflow.com/questions/13163480/nscache-and- фон - person ipmcc; 16.12.2013