#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
}];
}
Этот способ реализации асинхронных функций наиболее популярен в настоящее время. Но также вы можете использовать шаблон делегирования и уведомления.