Как вернуть текущий метод в блок GCD? - PullRequest
0 голосов
/ 17 мая 2018

Вот мой код:

- (void)viewDidLoad {
    [super viewDidLoad];

    [self testGCD];
}

- (void)testGCD {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSLog(@"1");
        return;
    });
    NSLog(@"2");
}

На консоли напечатано 1 и 2.

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

Можно ли как-нибудь вернуться из текущего метода в этом блоке GCD?

Ответы [ 5 ]

0 голосов
/ 17 мая 2018

Если вы увидите определение dispatch_once, то увидите, что они используют DISPATCH_EXPECT для сравнения onceToken.Вы также можете использовать if (onceToken != -1), но DISPATCH_EXPECT оптимизирует код, сообщая компилятору, что вероятность onceToken == -1 намного выше.Это называется предсказание ветвления

- (void)testGCD {
  static dispatch_once_t onceToken;
  if (DISPATCH_EXPECT(onceToken, ~0l) != ~0l) {
    dispatch_once(&onceToken, ^{
      NSLog(@"1");
      return;
    });
  }
  else {
    NSLog(@"2");
  }
}
0 голосов
/ 17 мая 2018

Используйте блок, чтобы обойти проблему.

- (void)testGCD {
    __block void(^codeBlock)(void) = ^{ NSLog(@"2"); };
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        codeBlock = ^{ NSLog(@"1"); };
    });
    codeBlock();
}
0 голосов
/ 17 мая 2018

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

- (void)testGCD {
    static BOOL didTrigger = NO;

    if(didTrigger) {
        NSLog(@"2");
    }
    else {
        didTrigger = YES;
        NSLog(@"1");
    }
}

Вы можете попытаться установить простую блокировку в вашем случае, но я не уверен, насколько безопасна в этом случае:

- (void)testGCD {
    static dispatch_once_t onceToken;
    static BOOL didInvokeOnceBlock = NO;
    static BOOL didPassSkippedBlock = NO;
    dispatch_once(&onceToken, ^{
        NSLog(@"1");
        didInvokeOnceBlock = YES;
    });
    if(didInvokeOnceBlock && didPassSkippedBlock) {
        NSLog(@"2");
    }
    didPassSkippedBlock = YES;
}

Но похоже, что при многопоточности результаты могут быть нестабильными. Возможно, вам придется запустить это атомарно, чтобы результат был правильным. Что я вижу в качестве потенциальной проблемы:

  • Поток A и поток B начинают выполнять один и тот же метод.
  • Нить А собирает токен и разблокирует didInvokeOnceBlock
  • Тема B пропускает блок и разблокирует didPassSkippedBlock, но пропускает NSLog(@"2");
  • Нить A вызывает NSLog(@"2");
0 голосов
/ 17 мая 2018
- (void)testGCD {
    static dispatch_once_t onceToken;
    __block NSString *text = @"2";
    dispatch_once(&onceToken, ^{
        text = @"1";
    });
    NSLog(@"%@", text);
}
0 голосов
/ 17 мая 2018

Оператор return возвращает текущую объемную область, которая в вашем случае является блоком.Вы не можете вернуться из внешней охватывающей области.

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

Что-то вроде:

- (void)testGCD {
    static dispatch_queue_t queue = dispatch_queue_create("com.example.MyQueue", NULL);
    static bool firstRun = YES;

    dispatch_sync(queue, ^{
       if (firstRun) {
           NSLog(@"1);
           firstRun = NO;
       } else {
           NSLog(@"2");
       }
    });
}

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

...