Reputation: 6158
Here is my code:
- (void)viewDidLoad {
[super viewDidLoad];
[self testGCD];
}
- (void)testGCD {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"1");
return;
});
NSLog(@"2");
}
The console printed 1 and 2.
What I want is only to print 1 the first time. I think maybe the return
is not returning from the method, but instead from the block.
Is there any way I can return from the current method in this GCD block?
Upvotes: 2
Views: 92
Reputation: 39978
If you see the definition of dispatch_once
then you gonna see that they are using DISPATCH_EXPECT
to compare the onceToken
. You can also use if (onceToken != -1)
but DISPATCH_EXPECT
optimises the code by telling the compiler that the probability of onceToken == -1
is much much higher. This is called Branch Prediction
- (void)testGCD {
static dispatch_once_t onceToken;
if (DISPATCH_EXPECT(onceToken, ~0l) != ~0l) {
dispatch_once(&onceToken, ^{
NSLog(@"1");
return;
});
}
else {
NSLog(@"2");
}
}
Upvotes: 2
Reputation: 4333
Use a block to get around the problem.
- (void)testGCD {
__block void(^codeBlock)(void) = ^{ NSLog(@"2"); };
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
codeBlock = ^{ NSLog(@"1"); };
});
codeBlock();
}
Upvotes: 3
Reputation: 4585
- (void)testGCD {
static dispatch_once_t onceToken;
__block NSString *text = @"2";
dispatch_once(&onceToken, ^{
text = @"1";
});
NSLog(@"%@", text);
}
Upvotes: 2
Reputation: 16774
Block is like a completely separate method. A return
in it will only return from the block. What you effectively seem to need is:
- (void)testGCD {
static BOOL didTrigger = NO;
if(didTrigger) {
NSLog(@"2");
}
else {
didTrigger = YES;
NSLog(@"1");
}
}
You might try to have a simple lock in your case but I am not sure how safe it would be in this case:
- (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;
}
But this still looks like it may have unstable results when multithreading. You might need to run this atomically for the result to be correct. What I see as potential issue is:
didInvokeOnceBlock
didPassSkippedBlock
but skips NSLog(@"2");
NSLog(@"2");
Upvotes: 0
Reputation: 114826
The return
statement returns from the current enclosing scope, which in your case is the block. You cannot return from the outer enclosing scope.
You can use a simple boolean flag to determine if this is the first time that the code has been executing, with a serial dispatch queue to ensure that it is thread-safe.
Something like:
- (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");
}
});
}
By using a serial dispatch queue you ensure that there cannot be concurrent update of firstRun
Upvotes: 0