Forcing the order of execution using dispatch_sync

半腔热情 提交于 2019-12-11 07:58:55

问题


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 :

  1. Caller (that I have no control over) calls createFile and immediately after calls readFile
  2. createFile fully executes, completion block C1 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!