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).
Существует два распространенных источника таких проблем:
Если это параллельная операция, не забудьте опубликовать
isFinished
КВН, например, при смене_finished
ivar, сделайте что-то вроде:[self willChangeValueForKey:@"isFinished"]; _finished = YES; [self didChangeValueForKey:@"isFinished"];
И, конечно же, у вас будет
isFinished
метод тоже:- (BOOL)isFinished { return _finished; }
Повторите этот процесс с
executing
, тоже.Лично я оборачиваю эту логику в
executing
а такжеfinished
Свойства, которые я могу поделиться, если вам это нужно, но я хотел сосредоточить этот ответ на основных требованиях (что у вас естьisFinished
метод, и вы публикуетеisFinished
КВН).Дополнительные сведения см. В разделе " Настройка операций для одновременного выполнения " главы "Очереди операций" в Руководстве по программированию параллелизма.
Убедитесь, что у вас нет сильного эталонного цикла (или цикла сохранения) внутри самой операции. Общие сильные опорные циклы включают в себя:
свойства класса, которые являются блоками, которые явно ссылаются
self
или неявно сделайте это, ссылаясь на некоторый ivar;повторяющиеся таймеры, для которых вы пренебрегаете
invalidate
до прекращения операции; или желюбые самоссылающиеся свойства (например, делегаты), которые случайно определены как
strong
,
В Инструментах вы можете использовать функцию "Запись количества ссылок" (описанную в последней части этого ответа), чтобы определить, что, если вообще что-то, поддерживает строгую ссылку на вашу работу.