Цель C: определение переменных внутри блока

#asynchronous #scope #objective-c-blocks

#асинхронный #область видимости #objective-c-блоки

Вопрос:

У меня есть функция objective C, которая выглядит следующим образом:

 - (BOOL)logInUser:(NSString*)user password:(NSString*)pass
{
__block BOOL ret;

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@/login", kBaseUrl]]];
[request addValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
request.HTTPMethod = @"POST";

NSDictionary *dict = @{ @"username":user, @"password":pass };
request.HTTPBody = [[self urlEncodedStringFromDict:dict] dataUsingEncoding:NSUTF8StringEncoding];


[[self.session dataTaskWithRequest:request completionHandler:
^(NSData *data, NSURLResponse *response, NSError *error)
{
    if (error == nil) {
        NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
        NSLog(@"logIn statuscode=%ld", (long)httpResponse.statusCode);
        ret = YES;
    } else {
        NSLog(@"logIn error=%@", error);
        ret = NO;
    }
}] resume];

return ret;
}
  

где сеанс определяется в заголовке. Я пытаюсь изменить значение ret внутри блока, так как у меня есть идентификатор __block в объявлении, но он не сохраняет значение за пределами блока, я думаю, это как-то связано с тем фактом, что я передаю блок в качестве параметра, но яя не уверен.

Ответ №1:

Да, блок может изменять значение __block переменной, и это изменение видно «за пределами блока». Вы в замешательстве, потому что вы не видите изменений при возврате ret из своей функции, но это только потому, что блок еще не запущен в этот момент, потому что он выполняется асинхронно.

Комментарии:

1. о, я вижу, значение изменяется после оператора return . Как мне получить информацию из оператора block перед оператором return?

Ответ №2:

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

 - (void)logInUser:(NSString *)user
         password:(NSString *)pass
       completion:(void (^)(BOOL isSuccess))completion
{
    // ...
    [[self.session dataTaskWithRequest:request
                     completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        // ...
        if (completion) {
            completion(YES);  // or NO - depends on code above
        }
    }] resume];
}
  

Использование:

 - (IBAction)loginDidPress
{
    // TODO: show activity indicator

    UserManager *userManager = [UserManager new];
    [userManager logInUser:self.userTextField.text
                  password:self.passwordTextField.text
                completion:^(BOOL isSuccess) {
        // TODO: hide activity indicator
        // TODO: implement further logic based on result
    }];
}
  

Этот способ реализации асинхронных функций наиболее популярен в настоящее время. Но также вы можете использовать шаблон делегирования и уведомления.