Take the 2-minute tour ×

I'm dealing with a problem here. There's this view that I use to see thumbnails of a document in my app. Since loading the thumbnails slowed the main thread, I looked for workarounds and ended up on doing an NSOperation for the thumbnail creation task.

I'm displaying a view with empty thumbnail frames and the corresponding activity indicator to tell the user "hold on, they're on their way". But this it's taking so long that I'm thinking of putting some elevator music to make the wait more pleasant x_X.

I have this NSOperationQueue set with 10 max concurrent operations. Setting that helped a little with the loading part, but just a little tiny little. Loading a single thumb it still taking like 6 seconds and that's and the weird thing it's that loading 10 takes the same. The following code is the operation itself

@class ThumbnailView;

@protocol LoadThumbnailOperationDelegate;
@interface LoadThumbnailOperation : NSOperation {
    NSString *key;
    id<LoadThumbnailOperationDelegate> delegate;
    CGSize _size;
    CGPDFDocumentRef _docRef;
    UIImage * _image;
    NSInteger _page;

@property (nonatomic,retain) NSString * key;
@property (nonatomic,assign) id<LoadThumbnailOperationDelegate>delegate;
-(id)initWithPage:(NSInteger)page  operationKey:(NSString *)opKey fromDocRef:(CGPDFDocumentRef)docRef size:(CGSize)size delegate:(id<LoadThumbnailOperationDelegate>)aDelegate;
-(NSInteger)getPage ;
@protocol LoadThumbnailOperationDelegate <NSObject>

-(void)operation:(LoadThumbnailOperation*)operation finishedLoadingThumbnail:(UIImage*)image;

@interface LoadThumbnailOperation (private)


@implementation LoadThumbnailOperation
@synthesize key;
@synthesize delegate;


    self = [super init];
    if (self) {

        self.key = opKey;
        _docRef = docRef;
        _size = size;
        _page = page;
        self.delegate = delegate;

    return self;

-(void)main {
    NSLog( @"LoadThumbnailOperaiton.m -> main key:%@",key);
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
    if (![self isCancelled]) {
        _image = [self makeThumbnailForPage:_page];
        [_image retain];
    if(![self isCancelled]){
        if ([delegate respondsToSelector:@selector(operation:finishedLoadingThumbnail:)]) {
            [delegate operation:self finishedLoadingThumbnail:_image];

    [pool release];

-(void)dealloc {

    [key release];
    [_image release];
    [super dealloc];

#pragma mark - 
#pragma mark graphics 

-(UIImage*) makeThumbnailForPage :(NSInteger) page  {
    NSLog( @"LoadThumbnailOperaiton.m -> makeThumbnailForPage:%d",page);
    CGPDFPageRef pdfPage = CGPDFDocumentGetPage(_docRef, page);
    if (pdfPage !=NULL){

    }else {
        NSAssert (pdfPage==NULL,@"pdf page NULL");

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef context = CGBitmapContextCreate(NULL, 
                                                 8,                      /* bits per component*/
                                                 _size.width * 4,   /* bytes per row */
                                                 kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
    CGContextClipToRect(context, CGRectMake(0, 0, _size.width,_size.height));

    CGRect pdfPageRect = CGPDFPageGetBoxRect(pdfPage, kCGPDFMediaBox);
    CGRect contextRect = CGContextGetClipBoundingBox(context);
    CGAffineTransform transform =aspectFit(pdfPageRect, contextRect);

    CGContextConcatCTM(context, transform);
    CGContextDrawPDFPage(context, pdfPage);

     create bitmap context
    CGImageRef image = CGBitmapContextCreateImage(context);
    UIImage *uiImage = [[UIImage alloc]initWithCGImage:image];

    // clean up
    [uiImage autorelease];
    return uiImage;

-(NSInteger)getPage {

    return _page;

This is the delegate method on the view controller that adds the images that have been loaded

NOTE: this app is for iPad only

Thank you in advance for your help

share|improve this question
Consider using GCD? –  James Bedford Feb 1 '11 at 16:51
GCD isn't going to magically going to add cores to the iPad.... –  bbum Feb 1 '11 at 17:37
add comment

2 Answers

up vote 3 down vote accepted

Well, I found a workaround to this issue. I solved it by checking whether the delegate call was done on the main thread or not. Apparently, the operations were done pretty fast, but the delegate call was not because it was not done on the main thread. So make sure you are making your delegate call on that thread by using the -[NSObject performSelectorOnMainThread:] method.

This still might not be fast for some customers. I ended up generating pdf thumbnails with automator and adding them to the app bundle and loading them from disk instead. Is much more faster than generating them on the ipad.

share|improve this answer
I was wondering why my KVO was not getting notified immediately. Solved the slowness, thank you. –  Plenilune Mar 28 '12 at 4:43
add comment

NOTE: this app is for iPad only

The iPad has one core.

Not only will cranking the concurrency up to 11 not make things any faster, it'll actually make it considerably slower due to bus contention, I/O limitations, and cache misses.

The only thing preventing your app from running slower is NSOperation; it is automatically throttling concurrency to prevent exactly the problems I mention from degrading performance far far worse than you are currently seeing.

share|improve this answer
I know that the iPad has a single core. And also know that NSOperations in this case are useful mostly to make the UI responsive in the meantime. What I'm saying is that it makes no sense that loading 1 thumbnail takes the same time than loading 8. It actually does take that with this implementation. so it means that I'm not doing things right somehow. This is not a rant about NSoperations at all. Sorry if it came out like that. I changed the title of my question to avoid further confusion –  Pacu Feb 1 '11 at 18:29
add comment

Your Answer


