NSOperationQueue取消特定操作

时间:2012-03-17 11:41:31

标签: ios multithreading nsoperationqueue nsinvocationoperation

问题是我管理了包含大量磁贴的scrollView。 每个可见的磁贴显示图像从URL加载或(在第一次URL加载后)缓存后台文件。隐形瓷砖回收(设置新框架和重绘)。

图像加载取决于图块位置。

对于长距离滚动,每个图块都会调用多次重绘:每个图块在显示正确的图像之前多次加载(并显示)不同的图像。

所以问题是在添加新内容之前取消之前添加的所有操作。

我将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];
}

在操作本身,我检查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];

    }

但它什么都不做。有时早期返回有效,但是瓷砖仍会在正确的图像之前加载许多图像。

那我怎么能成功呢? 滚动时,这些大量不必要的操作是否会导致主线程延迟? (因为延迟存在,我不知道为什么......所有负载都在背景中..)

更新:

使用NSLog: 执行时isCancelled: > 取消loadImage方法: >

取消工作。

现在我保存对TileView对象中最后一个操作的引用,并且只有在被调用的操作等于TileView操作时才执行setImage操作。

没有任何区别......

看起来有很多操作可以将不同的图像加载到一个接一个调用的图块中。

还有其他建议吗?

清关:

有单例DataLoader(来自它的所有代码)。并且所有图块都在drowRect中调用它:

[[DataLoader sharedDataLoader] loadWithColler:self];

更新

NSInvocationOperation子类:

@interface NSInvocationOperationWithContext : NSInvocationOperation {
    id context;
}

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

@end


@implementation NSInvocationOperationWithContext

@synthesize context;


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

非常感谢任何帮助!

SOLUTION:

从下面的回答:需要从NSOperation继承

当我将NSOperation子类化并将所有loadImage:代码放入“main”方法时(只需在此处移动所有代码而不是其他任何内容)并且所有工作都完美无缺!

关于滚动延迟:它会导致将图像加载到UIImageView(由于解压缩和栅格化(我理解)需要很长时间。

更好的方法是使用CATiledLayer。它在后台加载数据并且速度更快。

2 个答案:

答案 0 :(得分:1)

NSOperationQueue与“setSuspended”相关的方式是它不会开始运行在该点之后添加到它的新添加的NSOperations,并且不会开始运行当前在其中的任何那个尚未开始运行。您确定您尝试取消的操作尚未开始吗?

此外 - 您的NSOperation子类是否正确处理了键值观察?并发队列子类NSOperations必须为某些属性here调用willChangeValueForKeydidChangeValueForKey - 但由于您的队列未设置isConcurrent,因此不会出现问题。如果你走这条路,请跟我们一样。

答案 1 :(得分:1)

主线程的延迟是由于滚动时runloop的模式。我建议你观看wwdc2011网络应用会话。我不知道是否可以继承作为NSOperation的具体子类的NSInvocationOperation。我将改为NSOperation的子类。根据我的经验,如果你想避免缓慢滚动,你应该创建NSOperation子类,在特定的线程上加载它们的主要用于网络操作(你必须创建它)。有一个来自apple https://developer.apple.com/library/ios/#samplecode/MVCNetworking/Introduction/Intro.html

的精彩示例代码