0

问题是我管理滚动视图,里面有很多图块。每个可见的平铺显示从 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 while execution: > 取消 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

非常感谢您的帮助

解决方案:

从下面的答案:需要从 NSOperation 子类化

当我将 NSOperation 子类化并将所有 loadImage: 代码放入“main”方法时(只需将所有代码移至此处,仅此而已),一切都完美无缺!

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

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

4

2 回答 2

1

NSOperationQueue 对“setSuspended”的工作方式是它不会开始运行新添加的 NSOperations 添加到它之后,并且不会开始运行任何当前在其中尚未开始运行的东西. 您确定您要取消的操作尚未开始吗?

另外-您的 NSOperation 子类是否正确处理键值观察?Concurrent Queue 子类化 NSOperations 必须调用这里willChangeValueForKeydidChangeValueForKey某些属性- 但看起来这不是问题,因为您的队列没有设置。如果你走那条路,仅供参考。isConcurrent

于 2012-03-17T12:22:25.163 回答
1

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

于 2012-03-17T12:32:56.217 回答