问题
My library exposes 2 APIs as follows to:
-(void) createFile{
dispatch_sync(queueSerial, ^{ //B1
[fileObj createFileInfo:file completion:^(NSError *error){
//execute completion block C1
}];
});
}
-(void) readFile:(NSData*)timeStamp{
dispatch_async(queueSerial, ^{ //B2
[fileObj readFileInfo:fileName completion:^(NSError *error) {
dispatch_async(queueSerial2, ^{
//execute completion block C2
});
}]
});
}
Both readFile
and createFile
are asynchronous methods.
I usually recommend to the people using my library to call createFile
before readFile
. However, there is no guarantee how the callers will end up implementing this. It usually gets invoked in the following fashion (and I have no control over this)
[fileClass createFile];
[fileClass readFile:timeStamp];
What I want to do is to ensure readFile
gets called after the completion block C1
is executed. I also don't want to block the main thread with createFile
(but this expectation can be relaxed). So what I want to achieve as end result is :
- Caller (that I have no control over) calls
createFile
and immediately after callsreadFile
createFile
fully executes, completion blockC1
gets fired and after that,readFile
is dispatched to do it's thing.
How can I achieve this?
回答1:
It feels like you're trying to take an existing completion-handler based API and shoe-horn it into a serial queue pattern. But serial queues only make sense when the dispatched blocks are, themselves, synchronous. (And don't confuse this with dispatch_sync
... we're talking about whether the task inside the block is asynchronous or not, not how you dispatched it to your queue.) But you're dealing with asynchronous methods being called from other asynchronous methods.
The basic pattern when dealing with methods that take completion handler blocks, is to eliminate the serial queue altogether (no GCD queue is needed or is useful when the tasks are already asynchronous) and just use completion handler blocks in your own methods, and make the last call in the most deeply nested block call your own completion handler.
For example:
- (void)foo:(void (^)())completion {
[fileObj setFileInfo:file completion:^(NSError *error) {
// do some other stuff
completion();
}];
}
- (void)bar:(void (^)())completion {
[fileObj getFileInfo:fileName completion:^(NSError *error) {
// do some other stuff
completion();
}];
}
And then you'd call it like so:
[self foo:^{
[self bar:^{
// do whatever you want when its all done
}];
}];
This means that it won't do any of the getFile
stuff until the setFile
stuff is done.
Having said this, I wonder what added value your setFile
and getFile
methods add above and beyond the FileObject
classes' own methods.
But the key is that when dealing with asynchronous methods, you can't easily coordinate them with a simple serial GCD queue. If you want dispatch queue like behavior for tasks that are, themselves, asynchronous, then you'd generally consider using operation queues instead (or promises/futures or something like that).
来源:https://stackoverflow.com/questions/42403595/forcing-the-order-of-execution-using-dispatch-sync