如何return GCD 块中的当前方法?

How to return current method in GCD block?

这是我的代码:

- (void)viewDidLoad {
    [super viewDidLoad];

    [self testGCD];
}

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

控制台打印出12.

我想要的只是第一次打印1。我想也许 return 不是 return 来自方法,而是来自块。

有什么方法可以return从这个 GCD 块中的当前方法?

来自当前封闭范围的 return 语句 returns,在您的例子中是块。您不能从外部封闭范围 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

块就像一个完全独立的方法。其中的 return 只会 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开始执行相同的方法。
  • 线程A收集令牌并解锁didInvokeOnceBlock
  • 线程 B 跳过块并解锁 didPassSkippedBlock 但跳过 NSLog(@"2");
  • 线程 A 调用 NSLog(@"2");
- (void)testGCD {
    static dispatch_once_t onceToken;
    __block NSString *text = @"2";
    dispatch_once(&onceToken, ^{
        text = @"1";
    });
    NSLog(@"%@", text);
}

使用块来解决问题。

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

如果你看到 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");
  }
}