0

問題は、たくさんのタイルを含む 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 操作を実行します。

違いはありません...

次々に呼び出される 1 つのタイルにさまざまな画像をロードする操作が多数あるようです。

他に提案はありますか?

クリアランスの場合:

シングルトン 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: コードを「メイン」メソッドに配置すると (すべてのコードをここに移動し、他には何も移動しません)、すべてが完璧に機能します!

スクロールの遅延について:UIImageViewに画像をロードする原因となります((私が理解しているように)解凍とラスタライズのために長い時間がかかります)。

より良い方法は、CATiledLayer を使用することです。バックグラウンドでデータをロードし、はるかに高速に実行します。

4

2 に答える 2

1

メインスレッドの遅延は、スクロール中のランループのモードによるものです。WWDC2011 ネットワーキング アプリ セッションを視聴することをお勧めします。NSInvocationOperationの具体的なサブクラスであるan をサブクラス化しても問題ないかどうかはわかりませんNSOperationNSOperation代わりにサブクラス化します。私の経験では、スクロールが遅くなるのを避けたい場合はNSOperation、ネットワーク操作のために特定のスレッドにメインをロードするサブクラスを作成する必要があります (作成する必要があります)。アップルの素晴らしいサンプル コードがあります https://developer.apple.com/library/ios/#samplecode/MVCNetworking/Introduction/Intro.html

于 2012-03-17T12:32:56.217 に答える
1

「setSuspended」に関して NSOperationQueue が機能する方法は、その時点以降に追加された新しく追加された NSOperations の実行を開始せず、まだ実行を開始していない現在の NSOperations の実行を開始しないことです。 . キャンセルしようとしている操作がまだ開始されていませんか?

また、あなたの NSOperation サブクラスは Key Value Observing を正しく扱っていますか? 並行キューのサブクラス化された NSOperations は、ここでいくつかのプロパティを呼び出す必要がwillChangeValueForKeyありますが、キューが設定されていないため、それが問題のようには見えません。あなたがそのルートに行くなら参考までに。didChangeValueForKeyisConcurrent

于 2012-03-17T12:22:25.163 に答える