NSOperationQueue取消特定操作



问题是我管理的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];
}

在操作本身中,我检查是否已取消:

    -(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:is执行时取消:>cancel loadImage方法:>

所以取消工作。

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

没有任何区别。。。

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

还有其他建议吗?

对于清除:

存在singleton 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

非常感谢的任何帮助!

解决方案:

根据以下答案:需要从NSOperation 中进行子类化

当我将NSOperation子类化并将所有loadImage:代码放入"main"方法中时(只需将所有代码移到这里,不做任何其他操作),所有工作都非常完美!

关于滚动延迟:它的发生是因为将图像加载到UIImageView(由于解压缩和光栅化(据我所知),这需要很长时间)。

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

主线程上的延迟是由于滚动时运行循环的模式造成的。我建议您观看WWDC2011网络应用程序会话。我不知道是否可以将NSInvocationOperation作为NSOperation的具体子类进行子类化。我将改为NSOperation的子类。根据我的经验,如果你想避免缓慢的滚动,你应该创建NSOperation子类,将它们的main加载到一个特定的线程上进行网络操作(你必须创建它)。苹果公司提供了一个很棒的示例代码https://developer.apple.com/library/ios/#samplecode/MVCNetworking/Introduction/Intro.html

NSOperationQueue相对于"setSuspended"的工作方式是,它不会开始运行在该点之后添加到其中的新添加的NSOperations,也不会开始运行其中当前尚未开始运行的任何操作。您确定要取消的操作尚未开始吗?

此外,您的NSOperation子类是否正确处理键值观测?并发队列子类NSOperations必须为此处的某些属性调用willChangeValueForKeydidChangeValueForKey,但这似乎不是问题所在,因为您的队列没有设置isConcurrent。如果你走那条路,仅供参考。

最新更新