简体   繁体   English

NSOperationQueue 取消特定操作

[英]NSOperationQueue cancel specific operations

The problem is that I manage scrollView with lots of tiles in it.问题是我管理的 scrollView 中有很多图块。 Each visible tile display image loaded from URL or (after first URL load) cached file in background.每个可见图块显示从 URL 加载的图像或(在第一次 URL 加载后)后台缓存文件。 Invisible tiles recycles (set new frame and redraw).隐形瓷砖回收(设置新框架并重绘)。

Image load depends on tile position.图像加载取决于平铺位置。

With long range scroll there is multiple redraw called for each tile: each tile loads (and display) different image several times before display the correct one.对于长距离滚动,每个图块都会调用多次重绘:每个图块在显示正确的图像之前多次加载(并显示)不同的图像。

So problem is to cancel all previously added operations for tile before add new.所以问题是在添加新之前取消所有先前添加的 tile 操作。

I subclass NSInvocationOperation just to contain context object to detect operation attached to and before add new operation I canceling all operation for same tile:我子类化 NSInvocationOperation 只是为了包含上下文对象来检测附加到的操作,在添加新操作之前我取消了同一个图块的所有操作:

 -(void)loadWithColler:(TileView *)coller {    
    if (queue == nil) {
        queue = [NSOperationQueue new];
    }

    NSInvocationOperationWithContext *loadImageOp = [NSInvocationOperationWithContext alloc];
    [loadImageOp initWithTarget:self selector:@selector(loadImage:) object:loadImageOp];
    [loadImageOp setContext:coller];

    [queue setSuspended:YES];
    NSArray *opers = [queue operations];
    for (NSInvocationOperationWithContext *nextOperation in opers) {

        if ([nextOperation context] == coller) {
            [nextOperation cancel];
        }

    }

    [queue addOperation:loadImageOp]; 
    [queue setSuspended:NO];    
    [loadImageOp release];
}

And in operation itself I check isCancelled:在操作本身中,我检查 isCancelled:

    -(void)loadImage:(NSInvocationOperationWithContext *)operation {

        if (operation.isCancelled) return;

        TileView *coller = [operation context];

        /* TRY TO GET FILE FROM CACHE */    
        if (operation.isCancelled) return;

        if (data) {

            /* INIT WITH DATA IF LOADED */

        } else {
            /* LOAD FILE FROM URL AND CACHE IT */
        }

        if (operation.isCancelled) return;

        NSInvocationOperation *setImageOp = [[NSInvocationOperation alloc] initWithTarget:coller selector:@selector(setImage:) object:cachedImage];
        [[NSOperationQueue mainQueue] addOperation:setImageOp];
        [setImageOp release];

    }

But it is do nothing.但它什么都不做。 Some times early returns works but tiles still load many images before the correct one.有时提前返回有效,但图块仍会在正确的图像之前加载许多图像。

So how could I success?那我怎么能成功呢? And could this lots of unneeded operations cause delays on main thread when scrolling?滚动时这么多不需要的操作会导致主线程延迟吗? (Because delays are exists and I do not know why...all load in background..) (因为存在延迟,我不知道为什么......所有在后台加载......)

Update:更新:

With NSLog: isCancelled while executing: > cancel loadImage method for: >使用 NSLog: isCancelled 执行时: > 取消 loadImage 方法: >

So canceling work.所以取消工作。

Now I save reference to last operation in TileView object and perform setImage operation only if invoked operation is equal to TileView operation.现在我保存对 TileView 对象中最后一个操作的引用,并且仅当调用的操作等于 TileView 操作时才执行 setImage 操作。

Doesn't make any difference...没什么区别...

Looks like there IS number of operations to load different images to one tile invoked one after another.看起来有很多操作可以将不同的图像加载到一个接一个调用的图块。

Any another suggestions?还有其他建议吗?

For clearance:清关:

There is singleton DataLoader (all code from it).有单例 DataLoader(来自它的所有代码)。 And all tiles has call to it in drowRect:并且所有图块都在 drowRect 中调用它:

[[DataLoader sharedDataLoader] loadWithColler:self];

Update:更新:

NSInvocationOperation subclass: NSInvocationOperation 子类:

@interface NSInvocationOperationWithContext : NSInvocationOperation {
    id context;
}

@property (nonatomic,retain,readwrite) id context;

@end


@implementation NSInvocationOperationWithContext

@synthesize context;


- (void)dealloc
{
    [context release];
    [super dealloc];
}
@end

Thanks a lot for any help!非常感谢您帮助!

SOLUTION:解决方案:

From answer below: need to subclass from NSOperation从下面的答案:需要从 NSOperation 子类化

As I subclass NSOperation and put all loadImage: code into it "main" method (just move all code here and nothing else) and all work just perfect!当我继承 NSOperation 并将所有 loadImage: 代码放入它的“main”方法中时(只需将所有代码移到此处而没有其他任何内容),并且所有工作都非常完美!

As about scroll delaying: it occurs cause loading images to UIImageView (it takes long time because of decompress and rasterize (as I understood).至于滚动延迟:它发生导致将图像加载到 UIImageView (由于解压缩和光栅化(据我所知)需要很长时间)。

So better way is to use CATiledLayer.所以更好的方法是使用 CATiledLayer。 It loads data in background and do it much faster.它在后台加载数据并且加载速度更快。

The way that NSOperationQueue works with respect to "setSuspended" is that it won't start to run newly added NSOperations added to it after that point, and won't start to run any that are currently in it that haven't started running yet . NSOperationQueue 与“setSuspended”相关的工作方式是它不会开始运行在那之后添加到它的新添加的 NSOperations,并且不会开始运行任何当前在其中但尚未开始运行的NSOperations . Are you sure your operations you're trying to cancel haven't already started yet?您确定您要取消的操作尚未开始吗?

Also - does your NSOperation subclass correctly deal with Key Value Observing?另外 - 您的 NSOperation 子类是否正确处理键值观察? Concurrent Queue subclassed NSOperations have to call willChangeValueForKey and didChangeValueForKey for some properties here - but doesn't look like that's the issue as your queue doesn't set isConcurrent .并发队列子类 NSOperations 必须为这里的某些属性调用willChangeValueForKeydidChangeValueForKey - 但看起来不是问题,因为您的队列没有设置isConcurrent Just FYI if you go that route.如果你走那条路,仅供参考。

The delays on main thread is due to a the mode of the runloop while you scroll.主线程的延迟是由于滚动时运行循环的模式。 I suggest you to watch the WWDC2011 networking app sessions.我建议您观看 WWDC2011 网络应用程序会议。 I don't know if it is fine to subclass an NSInvocationOperation that is a concrete subclass of NSOperation .我不知道将NSInvocationOperation子类化是否可以,它是NSOperation的具体子类。 I will subclass NSOperation instead.我将NSOperation For my experience if you like to avoid sluggish scrolling, you should create NSOperation subclasses that load their main on a specific thread for networking operation (you must create it).根据我的经验,如果您想避免缓慢滚动,您应该创建NSOperation子类,将它们的主要加载到特定线程以进行网络操作(您必须创建它)。 There is a wonderful sample code from apple https://developer.apple.com/library/ios/#samplecode/MVCNetworking/Introduction/Intro.html苹果有一个很棒的示例代码https://developer.apple.com/library/ios/#samplecode/MVCNetworking/Introduction/Intro.html

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM