使用NSOperation的速度异常缓慢

3

我这里遇到了一个问题。在我的应用程序中,有一个视图显示文档缩略图。由于加载缩略图会使主线程变慢,因此我寻找解决方法,最终采用了NSOperation来创建缩略图。

我正在显示一个带有空缩略图框架和相应活动指示器的视图,告诉用户“等待中,请稍候”。但是这个过程太长了,以至于我考虑加上电梯音乐使等待更愉快x_X。

我设置了一个NSOperationQueue,最大并发操作数为10。设置这个值在一定程度上有助于加载,但只是微不足道的帮助。加载单个缩略图仍然需要大约6秒钟,而奇怪的是,加载10张缩略图也需要同样长的时间。以下是操作本身的代码。

@class ThumbnailView;

@protocol LoadThumbnailOperationDelegate;
@interface LoadThumbnailOperation : NSOperation {
    NSString *key;
    id<LoadThumbnailOperationDelegate> delegate;
    @private
    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;
@end


@interface LoadThumbnailOperation (private)
-(UIImage*)makeThumbnailForPage:(NSInteger)page;

@end

@implementation LoadThumbnailOperation
@synthesize key;
@synthesize delegate;

-(id)initWithPage:(NSInteger)page  operationKey:(NSString *)opKey fromDocRef:(CGPDFDocumentRef)docRef size:(CGSize)size delegate:(id<LoadThumbnailOperationDelegate>)aDelegate{

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

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

    }
    return self;
}

-(void)main {
#if DEBUG
    NSLog( @"LoadThumbnailOperaiton.m -> main key:%@",key);
#endif
    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];
    CGPDFDocumentRelease(_docRef);
    [_image release];
    [super dealloc];

}
#pragma mark - 
#pragma mark graphics 


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

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

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef context = CGBitmapContextCreate(NULL, 
                                                 _size.width, 
                                                 _size.height, 
                                                 8,                      /* bits per component*/
                                                 _size.width * 4,   /* bytes per row */
                                                 colorSpace, 
                                                 kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
    CGColorSpaceRelease(colorSpace);
    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);
    CGContextRelease(context);
    UIImage *uiImage = [[UIImage alloc]initWithCGImage:image];

    // clean up
    [uiImage autorelease];
    CGImageRelease(image);
    CGPDFPageRelease(pdfPage);
    return uiImage;
}

-(NSInteger)getPage {

    return _page;
}
@end

这是视图控制器上添加已加载的图片的委托方法。
注意:此应用仅适用于iPad。
感谢您的帮助。

3
GCD并不能神奇地为iPad增加核心数... - bbum
2个回答

3

我找到了一个解决这个问题的方法。通过检查委托调用是否在主线程上完成,我解决了这个问题。显然,操作非常快,但是委托调用不快,因为它没有在主线程上完成。因此,请确保您使用-[NSObject performSelectorOnMainThread:]方法在该线程上进行委托调用。

对于某些客户来说,这仍然可能不够快。最终,我使用自动化程序生成PDF缩略图,并将其添加到应用程序包中,从磁盘加载它们而不是在iPad上生成它们。这比在iPad上生成它们要快得多。


1
我一直在想为什么我的KVO没有立即得到通知。已经解决了慢的问题,谢谢。 - Morrowless

1

注意:此应用仅适用于iPad

iPad只有一个核心。

将并发性提高到11不仅不能使事情更快,反而会因总线争用、I/O限制和缓存未命中而使其明显变慢。

防止您的应用程序运行变慢的唯一方法是使用NSOperation;它会自动限制并发性,以防止我提到的问题严重影响性能,远远超出您当前看到的问题。


我知道iPad只有一个内核。并且也知道在这种情况下,NSOperations主要用于同时使UI响应。我的意思是,加载一个缩略图与加载8个缩略图的时间相同是没有意义的。但实际上,使用这种实现确实需要相同的时间。这意味着我做错了什么。这不是对NSOperations的抱怨。如果让人误解了,我很抱歉。我更改了我的问题标题以避免进一步的混淆。 - Pacu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接