I am using a proxy which is making asynchronous network calls. The response is to be processed and passed as an argument to the method in the next line. The code illustration is below:
- (MyClass *) performOperations{
...
...
[self callProxyMethodsTarget:self action:@selector(receivedValue:) withObject:anObject]; //making an asynchronous call here
return response;
}
- (void) receivedValue:(id)response{ ///need this return value from network call to return from performOperartions
...
...
}
My problem is that as the proxy methods are making asynchronous network calls, I am not able to get the response in time to return it.
I have thought of many things, like:
- making the network calls synchronous but that would require a lot of rework and may introduce unforeseen issues
- changing the return type and pass the response from receivedValue, but can't do that as my client has defined these method names and return types and on other platforms the team is using synchronous calls
I have thought of a solution like:
- (MyClass *) performOperations{
...
...
[self callProxyMethodsTarget:self action:@selector(receivedValue:) withObject:anObject]; //making an asynchronous call here
///write a code here to wait until response is received
return response;
}
- (void) receivedValue:(id)response{ ///need this return value from net开发者_如何学编程work call to return from performOperartions
...
...
}
But have failed to do so.
I am assuming that performOperations
is not called from the main thread, because if it is then you are in trouble. Otherwise you can user the current run-loop like this:
- (MyClass *) performOperations{
_isValueReceived = NO;
...
...
[self callProxyMethodsTarget:self action:@selector(receivedValue:) withObject:anObject]; //making an asynchronous call here
// Start with 10ms time boxes
NSTimeInterval ti = 0.01;
// Wait until delegate did callback
while (!_isValueReceived) {
NSDate* date = [NSDate dateWithTimeIntervalSinceNow:ti];
// Let the current run-loop do it's magif for one time-box.
[[NSRunLoop currentRunLoop] runMode:NSRunLoopCommonModes
beforeDate:date];
// Double the time box, for next try, max out at 1000ms.
ti = MIN(1.0, ti * 2);
}
return response;
}
- (void) receivedValue:(id)response {
...
...
_isValueReceived = YES;
}
This works like a charm for your background threads, and is exactly how GNUStep implements synchronous method dispatches for distributed objects using asynchronous I/O, so should be quite close to how Cocoa does it internally as well.
It is however not safe to do on the main thread, since UIKit is not safe for re-entrance into the run loop in the default mode.
You'll need to slightly refactor your application. You can't wait where you want to for the result to arrive because that will block the main thread which means that events on the run loop will not be serviced. This is bad for two reasons
- the user will experience a locked up application
- most likely the asynchronous reply is posted to the run loop as an event. This means you'll never see it and you'll stay blocked forever.
You need to create some state for the application e.g. a flag proxyRequestMade
and set it when you make the request. The method would then return to the run loop without returning a value.
Then, when you receive the response, you need to process it and reset the flag. The reason you have the flag is that it might change the way you handle other UI events e.g. you might disable certain buttons while the request is being made.
Do the proxy methods have delegate protocols that indicate their action is complete?
If so -- I don't know your situation, but if you have control of the code then you may be able to add them -- then you can set the calling object as the proxy methods' delegate, call the proxy method at the end of the calling method, and resume when the delegate method was called.
Edit for sample code
Your methods can't work quite like they do now because the first one -- the one calling the proxy method -- can't return a value. It might need to have a delegate protocol for whatever's calling it.
- (void) performOperations {
...
if (proxyMethod == nil) {
ProxyMethod *proxyMethod = [[ProxyMethod alloc] init];
}
[proxyMethod callProxyMethodsTarget:self action:@selector(receivedValue:) withObject:anObject]; //making an asynchronous call here
}
//Assuming this the the delegate method from the proxy object
- (void) receivedValue:(id)response {
// Continue with calling thread here
// Return information to whatever called performOperations via
// a delegate method or a notification
}
精彩评论