NSOperationQueue отменить определенные операции - PullRequest
0 голосов
/ 17 марта 2012

Проблема в том, что я управляю scrollView с большим количеством плиток.Каждое видимое изображение мозаичного изображения загружается из URL или (после первой загрузки URL) из кэшированного файла в фоновом режиме.Невидимые плитки перезаписываются (задайте новый кадр и перерисовку).

Загрузка изображения зависит от положения плитки.

При прокрутке на большие расстояния для каждой плитки требуется несколько перерисовок: каждая плитка загружается (и отображается)несколько раз перед тем, как отобразить правильное изображениеи перед добавлением новой операции я отменил все операции для той же плитки:

 -(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 и выполняю операцию setImage, только если вызванная операция равна операции TileView.

Не имеет значения ...

Похоже, есть количество операций для загрузки разных изображений в одну плитку, вызванную одну за другой.

Есть еще какие-нибудь предложения?

Для очистки:

Есть синглтонный 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. Он загружает данные в фоновом режиме и делает это намного быстрее.

Ответы [ 2 ]

1 голос
/ 17 марта 2012

Задержки в главном потоке вызваны режимом runloop во время прокрутки. Я предлагаю вам посмотреть сеансы сетевого приложения wwdc2011. Я не знаю, если это нормально для подкласса NSInvocationOperation, который является конкретным подклассом NSOperation. Я подкласс NSOperation вместо этого. По моему опыту, если вы хотите избежать медленной прокрутки, вы должны создать подклассы NSOperation, которые загружают свои основные данные в определенный поток для работы в сети (вы должны создать его). Есть замечательный пример кода от apple https://developer.apple.com/library/ios/#samplecode/MVCNetworking/Introduction/Intro.html

1 голос
/ 17 марта 2012

Способ, которым NSOperationQueue работает в отношении "setSuspended", заключается в том, что он не начинает запускать новые добавленные NSOperations, добавленные к нему после этой точки, и не начинает запускать любые, которые в настоящее время находятся в нем еще не начал работать . Вы уверены, что ваши операции, которые вы пытаетесь отменить, еще не начались?

Кроме того, правильно ли работает ваш подкласс NSOperation с наблюдением значения ключа? Подклассные NSOperations с параллельной очередью должны вызывать willChangeValueForKey и didChangeValueForKey для некоторых свойств здесь - но не похоже, что это проблема, поскольку ваша очередь не устанавливает isConcurrent. Просто к вашему сведению, если вы идете по этому маршруту.

...