1

NOTE: I am using ARC

I have a lazy image loading technique that works brilliantly when loading images from a web url. However, I am having problems loading images from core data. Here is the method I am using:

I first alloc init my mutable arrays, load my data from core data and then store it in an array. Then I have this code:

NSOperationQueue *queue = [NSOperationQueue new];
NSInvocationOperation *operation = [[NSInvocationOperation alloc]
                                    initWithTarget:self
                                    selector:@selector(loadImage)
                                    object:nil];
[queue addOperation:operation];

And then...

- (void)loadImage {
    for (int i = 0; i < [myArrayFromCoreData count]; i++) {
        AnObject *myObject = [myArrayFromCoreData objectAtIndex:i];
        UIImage* image;
        if ([myObject.image length] == 0) {
            image = [UIImage imageNamed:@"default.png"];
        }
        else {
            image = [[UIImage alloc] initWithData:myObject.image];
        }
        [self performSelectorOnMainThread:@selector(displayImage:) withObject:[NSArray arrayWithObjects:image,myObject.myObjectId, nil] waitUntilDone:NO];
    }
}

- (void)displayImage:(NSArray*)array {
    [loadedImages setObject:[array objectAtIndex:0] forKey:[array objectAtIndex:1]];
    [self.myTable reloadData];
}

Up to here there is no lag and an NSLog shows that all my images are added to the myObjectLoadedImages array.

The problem I am having is there is a lag when scrolling the table view and it is sometimes causing a crash. Here is the code I am using to display the loaded images:

UIImage* image = [loadedImages objectForKey:myObject.myObjectId];
if (image != NULL) {
    myImageView.image = image;
}
else {
    myImageView.image = [UIImage imageNamed:@"default.png"];
}

This is in the cellForRowAtIndexPath method so it is being called every time the cell is displayed. Is there something I am doing wrong here as my code is adapted from a class which is working fine loading images from a web url.

Patrick
  • 6,495
  • 6
  • 51
  • 78
  • 1
    The code does not appear to have any obvious bugs in it. That said, you could use a mutable dictionary to store the images, with the objectID as the key and the image as the data - that would really cut down on the amount of code with no change to its functionality. I think you should post uyour whole cellForRowAtIndexPath and mention if you are using ARC or not. Also reloading the whole table for just just one new image is really inefficient - you should figure out what visible cell needs updating and just do that one. – David H Aug 07 '12 at 22:52
  • Great advice to use an NSMutableDictionary! Made it 50% better. Now once the images have loaded once, it is smooth. But it is still slow the very first time images are loaded into the tableviewcell – Patrick Aug 07 '12 at 23:39

2 Answers2

1

First thought - you're not caching your UIImages, so they're not loaded from memory.

Have you tried https://github.com/nicklockwood/AsyncImageView/ ? In my opinion it's the best library for loading async images to any view, either from documents and web. It supports cache, progress views and many, many things that simples the code in controllers.

Sebastian Łuczak
  • 1,116
  • 8
  • 19
  • Looks great! Any idea if this can work with loading image data from core data, rather than a plist? – Patrick Aug 07 '12 at 22:56
  • Have you tried to get data from CoreData as NSData and just put it in UIImage with AsyncImageView? I think it's the simplest way. – Sebastian Łuczak Aug 07 '12 at 23:02
  • I am not sure how. I tried this `AsyncImageView *imageView = (AsyncImageView *)[cell viewWithTag:4]; imageView.image = [UIImage imageWithData:myObject.image];` but it crashed – Patrick Aug 08 '12 at 08:49
0

as i understand you have the image paths in core data and want to show the images in a UITableView, right ?

The lazy loading part should probably take place in the cellForRowAtIndexPathmethod. Have a look here: loading images from a background thread using blocks that could help

Basically you should use a NSFetchedResultsControllerto ge the paths out of core data. He does all caching etc for you. Once you have the paths, lazy load the images as said in the link above

Community
  • 1
  • 1
HeikoG
  • 1,793
  • 1
  • 20
  • 29
  • That looks good, I will try it out. I am actually saving the image data in core data - as the images are taken either from an URL or from the camera - it also has to run offline. – Patrick Aug 07 '12 at 22:37
  • 1
    well you probably dont want to store images larger than -say- icon size directly in coreData (or any SQLite Database). you could consider writing the images to the disc and only store the paths to the images in core data. You should get better performance by doing that – HeikoG Aug 07 '12 at 22:39
  • good tutorial on NSFetchedResultsController: http://www.raywenderlich.com/999/core-data-tutorial-how-to-use-nsfetchedresultscontroller – HeikoG Aug 07 '12 at 22:39