IOS / Objective-C: получить обратный вызов из асинхронного метода - PullRequest
0 голосов
/ 06 сентября 2018

В какой-то момент в моем приложении я вызываю метод в суперклассе для аутентификации на сервере.В конце этого метода я хотел бы запустить некоторый код, специфичный для класса, который его вызывает.Это лучший способ дождаться ответа от суперкласса, прежде чем вызывать дополнительный код?

     [super authenticateWithServer^(BOOL success, NSError *error) {
             dispatch_async(dispatch_get_main_queue(), ^{
             NSLog(@"heard back from method");
if (success==YES) {
//RUN MY CODE HERE
}
             });
             }];

Если это так, как будет выглядеть метод?Что-то вроде следующего?

-(BOOL)authenticateWithServer (
//if fail {
return NO;
}
else {
return YES;
}
}

1 Ответ

0 голосов
/ 07 сентября 2018

BOOL возвращаемые типы не смешиваются с асинхронными операциями. Вместо этого вы захотите передать результат аутентификации на сервере в блок завершения и попросить вашего вызывающего абонента проверить его. Я очень рекомендую взглянуть на http://goshdarnblocksyntax.com для правильного синтаксиса блока.

Я почти уверен, что это близко к тому, что вы пытаетесь сделать (здесь добавлена ​​фиктивная задержка для имитации запроса к серверу):

@interface ViewController ()
@property (strong, nullable) IBOutlet UILabel *resultLabel;
- (void)authenticateWithServer:(void (^_Nullable)(BOOL success, NSError *_Nullable error))completion;
- (void)_fakeUrlRequestToServerWithCompletion:(void(^_Nullable)(BOOL successFromServer, NSError *_Nullable errorFromServer))serverCompletion;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}

- (IBAction)authWithServerButton:(id)sender {
    // call to authenticate with the results in the callback (not returning BOOL)
    [self authenticateWithServer:^(BOOL success, NSError * _Nullable error) {
        // callback will probably come in off the main queue so if you're doing some UI updates jump back on the main queue
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            self.resultLabel.text = [NSString stringWithFormat:@"Result = %@\nError = %@",success == 1 ? @"SUCCESS" : @"FAILURE", error == nil ? @"No Error" : error];
        }];
    }];
}

- (void)authenticateWithServer:(void (^_Nullable)(BOOL success, NSError *error))completion {
    // put this on the background queue so it doesn't hug
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
        // call the fakeUrlRequestToServer method to simulate your async request
        [self _fakeUrlRequestToServerWithCompletion:^(BOOL successFromServer, NSError * _Nullable errorFromServer) {
            // completion block that was passed into the method, callback and pass along the success and error results
            completion(successFromServer, errorFromServer);
        }];
    });
}

- (void)_fakeUrlRequestToServerWithCompletion:(void(^_Nullable)(BOOL successFromServer, NSError *errorFromServer))serverCompletion {
    // fake sleep here for 2 seconds just to simulate waiting for the callback
    // never call sleep in your own code
    sleep(2);
    NSError *fakeError = nil;
    // just a fake auth success or failure
    BOOL fakeSuccess = arc4random() % 2 == 1 ? YES : NO;
    if (fakeSuccess == NO) {
        // fake error
        fakeError = [NSError errorWithDomain:@"FakeErrorDomain" code:22 userInfo:nil];
    }
    // completion block that was passed into the method, call back with the success and error params passed in
    serverCompletion(fakeSuccess, fakeError);
}


@end

Вот пример, если он в действии:

Fake Async Callback

Редактировать: , поскольку блоки завершения завершены в примере, который я привел, вы захотите проверить, был ли один передан первым.

т.е.

  if (completion) {
    completion(success, error);
  }
...