2015-12-15 2 views
1

Итак, я мог бы не полностью схватить КОГДА я должен использовать weakSelf в блоках. Я знаю, что это сделано для предотвращения циклов сохранения, а что нет, но я слышал, что есть некоторые исключения из этого правила.iOS - Должен ли я звонить «слабый» в этом блоке?

В следующем коде я проверяю, не удалось ли выполнить вызов API из-за истечения срока действия входа в систему, а затем я попытаюсь повторно аутентифицировать пользователя и повторить запрос API, который не удалось из-за этой проблемы, вызвав [self sendTask:request successCallback:success errorCallback:errorCallback]; в блок успеха повторного аутентификации:

/*! 
* @brief sends a request as an NSHTTPURLResponse. This method is private. 
* @param request The request to send. 
* @param success A block to be called if the request is successful. 
* @param error A block to be called if the request fails. 
*/ 
-(void)sendTask:(NSURLRequest*)request successCallback:(void (^)(NSDictionary*))success errorCallback:(void (^)(NSString*))errorCallback 
{ 
    NSURLSessionDataTask *task = [self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) 
    { 
     [self parseResponse:response data:data successCallback:success errorCallback:^(NSString *error) { 
      //if login session expired and getting "not authenticated" error (status 401) 
      NSHTTPURLResponse *httpResp = (NSHTTPURLResponse*) response; 
      if (httpResp.statusCode == 401) { 
       NSLog(@"NOT AUTHENTICATED THO"); 
       AuthenticationHelper* auth = [AuthenticationHelper sharedInstance]; 
       NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; 
       //attempt to re-authenticate the user 
       [AuthenticationHelper loginUser:[defaults stringForKey:@"username"] password:[defaults stringForKey:@"password"] successCallback:^(User* u) 
       { 
        auth.loggedInUser = u; 
        NSLog(@"RE-AUTHENTICATION BUG FIX SUCCEEDED"); 
        //attempt to re-try the request that failed due to 
        [self sendTask:request successCallback:success errorCallback:errorCallback]; 
       } errorCallback:^(NSString *error) { 
        NSLog(@"RE-AUTHENTICATION BUG FIX FAILED"); 
       }]; 
      } 
      else { 
       errorCallback(error); 
      } 
     }]; 
    }]; 
    [task resume]; 
} 

Является ли эта плохая практика тем, что она может привести к циклу удержания? Если да, то почему? Должен ли я вместо этого использовать [weakSelf sendTask:request successCallback:success errorCallback:errorCallback];, предполагая, что я делаю MyAPIInterface *__weak weakSelf = self; заранее?

ответ

2

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

+0

Я не понимаю, как само может содержать ссылку на блок. Можете ли вы привести пример этого, в котором это приведет к циклу сохранения? Простите меня, но я просто начал использовать блоки. – Rafi

+1

Точно так же он может содержать ссылку на любой объект - переменную или свойство экземпляра. – Avi

+0

@ Rafi - рассмотрите, что может произойти, если вместо локальной переменной 'task' вместо этого была переменная экземпляра: тогда у вас, возможно, есть' self' -> 'task' -> block ->' self' - который * потенциально * вызывает неприятный цикл (не все циклы плохие, особенно переходные). – CRD

1

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

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

В качестве одного из возможных примеров; предположим, что вы запускаете один из этих запросов, а затем пользователь выходил из системы и подписывался в другую учетную запись. Запрос может завершиться неудачей, а затем повторить попытку с использованием учетных данных второй учетной записи пользователя. Вероятно, это не тот результат, которого пользователь хотел.

Возможно, это действительно невозможно в этом приложении, но если вы используете сильные ссылки, подобные этому, вам нужно будет рассмотреть его и пересмотреть его каждый раз, когда вы измените способ взаимодействия приложения с этим объектом. Это приводит к тому, что класс трудно или даже опасен для повторного использования. Если вы вместо этого тщательно изучите strong и weak, вы можете написать класс, который не будет настолько связан с состоянием остальной части приложения.

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

__weak __typeof__(self) weakSelf = self

Это дает нам weakSelf, который будет ноль, если self было высвобождено перед тем блок завершения выполняет, однако в зависимости от модели параллельности это может быть возможным для self для освобождения в течение выполнения блока завершения.Поэтому мы обычно хотим создать сильную ссылку как нашу первую операцию внутри блока, так что если self все еще существует, когда блок начинает выполнять он будет оставаться до тех пор, блок не будет закончен, и это сильная ссылка отбрасывается:

__strong __typeof__(self) strongSelf = weakSelf

Ряд библиотек и проектов object-c извлекает эти инструкции в weakify и strongify макросах, которые могут быть полезны.

+0

Конечно, создание этой сильной ссылки в блоке приведет вас к первой проблеме удержания на 'self', пока блок не завершится, но теперь вы добавили прекрасное условие гонки в микс. – Avi

+0

В моем приложении нет способа войти в систему с учетными данными другого пользователя. Когда пользователь выходит из системы добровольно, то значения по умолчанию очищаются, а глобальный «loggedInUser» равен нулю. – Rafi

+0

Сохранение 'self' во время выполнения блока не является проблемой. Сохранение циклов является проблемой, когда блок фиксирует сильную ссылку на «self» и сохраняет ее даже тогда, когда она не выполняется. Я также не вижу здесь никаких условий гонки; 'strongSelf' будет существовать или быть nil на протяжении всего блока. – Jonah

Смежные вопросы