可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
How can I make a block execute synchronously, or make the function wait for the handler before the return statement, so the data can be passed back from the block?
-(id)performRequest:(id)args
{
__block NSData *data = nil;
[xyzclass requestAccessToAccountsWithType:accountType withCompletionHandler:^(BOOL granted, NSError *error) {
data = [NSData dataWithData:responseData];
}];
return data;
}
回答1:
You can use semaphores in this case.
-(id)performRequest:(id)args
{
__block NSData *data = nil;
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
[xyzclass requestAccessToAccountsWithType:accountType withCompletionHandler:^(BOOL granted, NSError *error) {
data = [NSData dataWithData:responseData];
dispatch_semaphore_signal(sem);
}];
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
return data;
}
semaphore will block execution of further statements until signal is received, this will make sure that your function does not return prematurely.
回答2:
async is almost always better. but if you want synchronous:
-(id)performRequest:(id)args
{
__block NSData *data = nil;
[xyzclass requestAccessToAccountsWithType:accountType withCompletionHandler:^(BOOL granted, NSError *error) {
data = [NSData dataWithData:responseData];
}];
while(!data) {
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
}
return data;
}
Disclaimer:
CocoaCouchDeveloper says that of course this will only work if the completion block and the runloop are on the same thread.
I assumed that because many(most) COMPLETION handler I know work that way but of it is valid in principle.
The above is not thread safe
use a semaphore or something maybe.
also I said I don't promote this
回答3:
You could just add a method which processes the returned data and call that in your block:
-(void)performRequest:(id)args{
__block NSData *data = nil;
[xyzclass requestAccessToAccountsWithType:accountType withCompletionHandler:^(BOOL granted, NSError *error) {
data = [NSData dataWithData:responseData];
[self processData:data]; //method does something with the data
}];
}
回答4:
You can do synchronous request in another thread like below code
-(void)performRequest:(id)args
{
NSURLResponse *response = nil;
NSError *error = nil;
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
}
From Main thread you can call this
[self performSelectorInBackground:@selector(performRequest:) withObject:args];
or else you can do asynchronous request using following method
[NSURLConnection alloc]initWithRequest:request delegate:self];
and implement delegate methods for NSURLConnection
回答5:
are you sure you want to do it synchronously ?
if yes, you can call (or put) your handler function in your block or use Jignesh advice (and use “performSelectorInMainThread” when your handler is finished and you want to return values.
the asynchronous way is (a little bit) harder, but better as:
- it forces you to write clean code (no passing of convenient variables)
- you can execute other thing so the users won't wait and think your app is slow.
you should really give it two or three hours to go asynchronous. small pain for full gain.
you can also have a look to Key-Value Observing.
回答6:
You could do like this.
-(id)performRequest:(id)args
{
__block NSData *data = nil;
[xyzclass performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
dispatch_sync( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),^{
data = [NSData dataWithData:responseData];
});
}];
return data;
}