NSOperationQueue не отменяет операцию после ее завершения

Я создаю задачу (NSOperation) и помещаю ее в NSOperationQueue

   TaskJsonParser *parseTask = [[TaskJsonParser alloc] initWithJsonString:responseString andDelegate:self];
    [self.opQueue addOperation:parseTask];

Когда задача завершается, она вызывает метод делегата и устанавливает для ее флагов значение "выполнено". Но Instruments показывает, что он все еще находится в памяти... И указывает на приведенный выше код как на создателя.

Почему очередь не освобождает ее после завершения операции. Нет другой сильной ссылки на это...

Это код NSOperation

@interface TaskJsonParser ()

@property (strong, nonatomic) NSString *stringToParse;
@property (strong, nonatomic) SBJson4Parser *jsonParser;

@end

@implementation TaskJsonParser

#pragma mark - Custom initialization

- (instancetype)initWithJsonString:(NSString *)jsonString andDelegate:(id<JsonParsingDelegate>) delegate
{
    self = [super init];
    if (self) {
        _delegate = delegate;
        _stringToParse = jsonString;
        self.TAG = @"TaskJsonParse";

        // configure the parser
        SBJson4ValueBlock block = ^(id v, BOOL *stop) {
            BOOL isDictionary = [v isKindOfClass:[NSDictionary class]];
            if (isDictionary) {
                NSDictionary *parseResult = (NSDictionary *)v;
               // [self.delegate didFinishParsingJsonWithResult:parseResult];
                [(NSObject *)self.delegate performSelector:@selector(didFinishParsingJsonWithResult:) withObject:parseResult];

            } else {
                NSLog(@"json parsing did not result in a NSDictionary, returnig %@",NSStringFromClass([v class]));
                [(NSObject *)self.delegate performSelector:@selector(didFinishParsingJsonWithResult:) withObject:v];
              //  [self.delegate didFinishParsingJsonWithResult:v];
            }
        };

        SBJson4ErrorBlock errorBlock = ^(NSError* err) {
            NSLog(@"OOPS parsing error: %@", err);
            MvpError *e = [[MvpError alloc] initWithErrorObject:err andType:Parser];
      //      [self.delegate didFailToParseJsonWithError:e];
            [(NSObject *)self.delegate performSelector:@selector(didFinishParsingJsonWithResult:) withObject:e];
        };

        _jsonParser = [SBJson4Parser multiRootParserWithBlock:block
                                                 errorHandler:errorBlock];

          if (debugLog) { NSLog(@"Allocating Task: %@;",self.TAG); }
    }
    return self;
}

#pragma mark - Overriden NSObject methods 
// requierd for concurrent running

- (void)main
{
    @autoreleasepool {
        if (self.isCancelled) {
            [self completeOperation];
            return;
        }

        SBJson4ParserStatus responseCode = [self.jsonParser parse:[self.stringToParse dataUsingEncoding:NSUTF8StringEncoding]];

        if (responseCode == SBJson4ParserComplete) {
            NSLog(@"parse complete");
        } else if (responseCode ==  SBJson4ParserError) {
            NSLog(@"failed to parse");
        }
        [self completeOperation];
     }
}

Может ли этот блок в методе init... вызвать проблему?

1 ответ

Решение

У вас сильный справочный цикл. В частности, у вас есть код, который говорит:

SBJson4ValueBlock block = ^(id v, BOOL *stop) {
    BOOL isDictionary = [v isKindOfClass:[NSDictionary class]];
    if (isDictionary) {
        NSDictionary *parseResult = (NSDictionary *)v;
       // [self.delegate didFinishParsingJsonWithResult:parseResult];
        [(NSObject *)self.delegate performSelector:@selector(didFinishParsingJsonWithResult:) withObject:parseResult];

    } else {
        NSLog(@"json parsing did not result in a NSDictionary, returnig %@",NSStringFromClass([v class]));
        [(NSObject *)self.delegate performSelector:@selector(didFinishParsingJsonWithResult:) withObject:v];
      //  [self.delegate didFinishParsingJsonWithResult:v];
    }
};

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

Вы можете исправить это, используя weakSelf шаблон:

typeof(self) __weak weakSelf = self;

SBJson4ValueBlock block = ^(id v, BOOL *stop) {
    BOOL isDictionary = [v isKindOfClass:[NSDictionary class]];
    if (isDictionary) {
        NSDictionary *parseResult = (NSDictionary *)v;
        [weakSelf.delegate didFinishParsingJsonWithResult:parseResult];
    } else {
        NSLog(@"json parsing did not result in a NSDictionary, returnig %@",NSStringFromClass([v class]));
        [weakSelf.delegate didFinishParsingJsonWithResult:v];
    }
};

Итог, воздержитесь от использования self внутри блоков strong собственности, и вы избежите вашего сильного референтного цикла. Повторите это редактирование в своем блоке ошибок тоже.


Кроме того, я заметил, что вы сделали JSON-анализатор свойством класса. Так как parse работает синхронно, это не нужно, и вы можете сделать синтаксический анализатор локальной переменной main, и это также решило бы ваш сильный справочный цикл, не нуждаясь в вышеупомянутом weakSelf шаблон.


Ниже приведен общий совет, который я первоначально предоставил перед вашим примером кода. Выпуск был первым пунктом пункта 2).


Существует два распространенных источника таких проблем:

  1. Если это параллельная операция, не забудьте опубликовать isFinished КВН, например, при смене _finished ivar, сделайте что-то вроде:

    [self willChangeValueForKey:@"isFinished"];
    _finished = YES;
    [self didChangeValueForKey:@"isFinished"];
    

    И, конечно же, у вас будет isFinished метод тоже:

    - (BOOL)isFinished
    {
        return _finished;
    }
    

    Повторите этот процесс с executing, тоже.

    Лично я оборачиваю эту логику в executing а также finished Свойства, которые я могу поделиться, если вам это нужно, но я хотел сосредоточить этот ответ на основных требованиях (что у вас есть isFinished метод, и вы публикуете isFinished КВН).

    Дополнительные сведения см. В разделе " Настройка операций для одновременного выполнения " главы "Очереди операций" в Руководстве по программированию параллелизма.

  2. Убедитесь, что у вас нет сильного эталонного цикла (или цикла сохранения) внутри самой операции. Общие сильные опорные циклы включают в себя:

    • свойства класса, которые являются блоками, которые явно ссылаются self или неявно сделайте это, ссылаясь на некоторый ivar;

    • повторяющиеся таймеры, для которых вы пренебрегаете invalidate до прекращения операции; или же

    • любые самоссылающиеся свойства (например, делегаты), которые случайно определены как strong,

    В Инструментах вы можете использовать функцию "Запись количества ссылок" (описанную в последней части этого ответа), чтобы определить, что, если вообще что-то, поддерживает строгую ссылку на вашу работу.

Другие вопросы по тегам