使用 NSOperation 出奇地慢

发布于 2024-10-15 22:25:38 字数 4178 浏览 8 评论 0原文

我正在这里处理一个问题。我用这个视图来查看应用程序中文档的缩略图。由于加载缩略图会减慢主线程的速度,因此我寻找解决方法,最终为缩略图创建任务执行了 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

提前感谢您的帮助

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;
    @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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

尐偏执 2024-10-22 22:25:38

出色地,
我找到了解决此问题的方法。我通过检查委托调用是否在主线程上完成来解决了这个问题。显然,操作完成得相当快,但委托调用却不是,因为它不是在主线程上完成的。因此,请确保您使用 -[NSObject PerformSelectorOnMainThread:] 方法在该线程上进行委托调用。

对于某些客户来说,这可能仍然不太快。我最终使用 automator 生成了 pdf 缩略图,并将它们添加到应用程序包中,然后从磁盘加载它们。比在 ipad 上生成它们要快得多。

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.

病女 2024-10-22 22:25:38

注意:此应用程序仅适用于 iPad

iPad 有一个核心。

将并发度提高到 11 不仅不会让速度变得更快,而且实际上会因总线争用、I/O 限制和缓存未命中而显着降低速度。

唯一阻止你的应用程序运行速度变慢的是 NSOperation;它会自动限制并发性,以防止我提到的问题导致性能下降远比您目前看到的更糟糕。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文