这是我的代码:
- (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 块中的当前方法返回?
使用块来解决问题。
- (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");
}
}
- (void)testGCD {
static dispatch_once_t onceToken;
__block NSString *text = @"2";
dispatch_once(&onceToken, ^{
text = @"1";
});
NSLog(@"%@", text);
}
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
只会从块中返回。您实际上似乎需要的是:
- (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");