Search code examples
multithreadinggrand-central-dispatchnsoperationnsoperationqueuenscache

How to use NSCache with CoreData


I have developed a project, where a user draws a image on a canvas, I store it in the file using CoreData, I have one-to-many relationship called folder-to-files. So here all are images. I retrive the images from files , resize according to my table cell height and show it on a table. Once it is shown, I want to cache the images.

I also have some labels on the folder cell, which give me some info regarding my files, which I update on fly. I also swipe the cells to mark it complete and move it to the bottom the cell.

I also show same file images in different Views depending on how user queries it.

I want to know the best method for this, I read through the web, their are many methods, GCD, NSOperationQueues and many more.

Which method will be best suited for me.

I want to show some code

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

So what is happening here is that configureCellWithNote is taking lot of time, because it is resizing images. Please help me out in deciding how can this performance issue be solved.

Regards Rajit


Solution

  • If you simply want to shuffle the resize operation to a background thread, you could do something like this:

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

    If you want to cache the results, then the trick will be to come up with a good cache key. Unfortunately, it's hard to tell from what you've posted what would make a good cache key. Certainly it will need to include the size, but it'll also need to include something that ties it back to the category. I suppose if nothing else you could use the NSManagedObjectID for the category, but I think that'll be specific to each managed object context you have. Assuming there was a property on Folder called uniqueName a caching implementation might look like this:

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