2015-07-16 3 views
0

Мое приложение отправляет много сообщений через HTTP запросов. Я написал простую обертку вокруг HTTP-запроса, у которого есть первичный метод, чтобы отменить запрос и вернуть результат NSData.ARC Async Block Best Practices

Это выглядит следующим образом:

- (void)sendRequest:(NSURLRequest *)request { 
    [[[NSURLSession sharedSession] dataTaskWithRequest:request 
            completionHandler:^(NSData *data, 
                 NSURLResponse *response, 
                 NSError *error) { 
             NSLog(@"---->> request returned"); 

             if (_delegate == nil) 
              return; 

             if (error == nil) { 
              // forward along 
              [_delegate requestReturnedResult:data withResponse:response]; 

             } 
             else { 
              // error occurred 
              [_delegate requestReturnedError:error withResponse:response]; 
             } 
            }] resume]; 
} 

Этот HttpRequestHelper объект является ivar из моего сообщения абстракции объекта OutboundMessage, что упаковывает данные, а затем отправляет его.

У меня есть объект MessageSender, который принимает некоторые данные, создает объект OutboundMessage и отправляет его.

Эта абстракция работает хорошо, за исключением того, что я думаю, что сталкиваюсь с проблемой с ARC.

Когда кто-то звонит [MessageSender send:], I alloc a OutboundMessage и отправляет его.

То, что я вижу через NSLog, что мой OutboundMessage очищается (предположительно, когда send выходит, то я вижу HttpRequestHelper очищены. Тогда я вижу NSLog в обработчике завершение моего dataTaskWithRequest.

Это указывает на то, что все, что было у моего помощника запроса, было очищено до завершения HTTP-запроса. Затем, как вы можете себе представить, в ближайшее время я попытаюсь добавить свой ответ через delegate, я получаю exc_bad_access.

Я могу t hink некоторых творческих способов предотвратить освобождение и предотвратить это, но то, что я хотел бы знать, концептуально является лучшим практическим приемом для обработки этого шаблона при отключении сообщений, когда обертывающие объекты очищаются только после завершения запроса.

Спасибо.

Update Каждый объект обертка был использованием initWithDelegate:self шаблонов стиля, и я заметил, эти делегаты были сохранены, как это:

@property (nonatomic, assign) id<HttpRequesterDelegate> delegate; 

Я изменил к ним strong и, кажется, чтобы исправить это, но я я не уверен, что сейчас я теряю память или если есть более оптимальный подход.

+0

Изменение его на сильный, скорее всего, приведет к утечке памяти, если вы не установите делегат в нуль где-нибудь после того, как звонок вернется. – dan

+0

Хм, так как я делаю 'initWithDelegate: self', было бы достаточно установить делегат на нуль в каждом' dealloc'? Каковы альтернативные решения этой проблемы? – Ternary

+0

'OutboundMessage' является объектом' NSdata'? Как вы создаете объект 'request' из' OutboundMessage'? –

ответ

1

Прежде всего, NSURLSessionDataTask объект, созданный NSURLSession - dataTaskWithRequest:completionHandler: метод, сохраняет completionHandler блок объект. Объект блока не освобождается до тех пор, пока блок не будет выполнен, когда задача данных будет завершена.

Далее блокирует захват (сохранение) локальных переменных автоматически.Следующая строка в коде

if (_delegate == nil) 

такое же, как

if (self.delegate == nil) 

delegate Если свойство strong

@property (nonatomic, strong) id<HttpRequesterDelegate> delegate; 

Затем self удерживается блоком. В этом случае self - HttpRequestHelper объект. Таким образом, объект HttpRequestHelper и объект delegate живут до тех пор, пока задача данных завершена. Это безопасно.

delegate Но свойство assign

@property (nonatomic, assign) id<HttpRequesterDelegate> delegate; 

Это то же самое, как __unsafe_unretained. Блоки не сохраняют объект __unsafe_unretained вообще. Таким образом, self не был сохранен, он будет выпущен до завершения задачи данных.

ДОБАВЛЕНО

В этом случае, вы можете использовать локальную переменную для сохранения delegate объекта. Поскольку для завершенияHandler используется только объект delegate, а не объект HttpRequestHelper.

id<HttpRequesterDelegate> delegate = _delegate; 

[[[NSURLSession sharedSession] dataTaskWithRequest:request 
           completionHandler:^(NSData *data, 
                NSURLResponse *response, 
                NSError *error) { 
            NSLog(@"---->> request returned"); 

            /* 
             * Use `delegate` instead of `_delegate` 
             */ 
            if (delegate == nil) 
             return; 

            ... 

В этом случае атрибут delegate собственности является сильным или переуступать, не имеет значения.

+0

Вы говорите в этом случае мой делегат должен быть сильным? – Ternary

+0

Да, он должен быть сильным. –

+0

Я думал, что типичный шаблон делегата должен был использовать назначение? – Ternary