问题
I've got the following class which makes an HTTP post request asynchronously to avoid problems on the main UI thread:
@implementation DataFeeder
-(void) doLookup:(NSString *)inputValue
{
NSString *myRequestString = [NSString stringWithFormat:@"val=%@", inputValue];
NSMutableData *myRequestData = [ NSMutableData dataWithBytes: [ myRequestString UTF8String ] length: [ myRequestString length ] ];
NSURL * myUrl = [NSURL URLWithString: @"http://mywebsite/results.php"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL: myUrl];
[request setHTTPMethod: @"POST"];
[request setHTTPBody: myRequestData];
[request setTimeoutInterval:10.0];
[[NSURLConnection alloc] initWithRequest:request delegate:self];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
responseData = [[NSMutableData alloc] init];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[responseData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
// Show error message
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// Use responseData
// Got all my response data here, so build up an object ready to send back
}
@end
I'm invoking the above from my ViewController using the following line of code :
MyObject * myObj = [feeder doLookup:@"SomeStaticStringForNow"];
So, this is how I understand it :
- The
doLookupwill execute the request on an asynchronous connection. - When the data has been fully loaded, it will invoke
connectionDidFinishLoading - Once the data has finished loading, I will build up an object from the response data that I will send back to the calling controller
How can I have the calling controller listen out for this? Do I need to implement my own callback methods in the ViewController that will listen out for invocation and then stop a spinner and update the UI based on the contents of myObj ?
I'm hoping theres a really easy way that I've overlooked...
Thanks
回答1:
Yeah, you should implement your callback using the delegate pattern. That is, in my opinion, the easiest and most standard way to do it. There are other ways, as you can see in the other responses.
In your DataFeeder.h file:
@protocol DataFeederDelegate
- (void)dataReady:(NSData*)data;
@end
@interface DataFeeder : NSObject {
id delegate_;
}
- (id)initWithDelegate:(id<DataFeederDelegate>)delegate;
@end
In your DataFeeder.m:
@implementation DataFeeder
- (id)initWithDelegate:(id<DataFeederDelegate>)delegate {
self = [super init];
if(self) {
delegate_ = delegate;
}
return self;
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[delegate_ dataReady:responseData];
}
@end
You would instantiate a DataFeeder object like this:
DataFeeder *dataFeeder = [[DataFeeder alloc] initWithDelegate:self];
Of course, the calling view controller has to implement the DataFeederDelegate methods.
回答2:
You have several approaches to getting you ViewController notified when the data is there:
define a delegate protocol between the
ViewControllerand theDataFeeder, so that the latter sends a message to the former inconnectionDidFinishLoading:;use
NSNotificationCenterso to decoupleDataFeederandViewController:ViewControlleradds itself as an observer to the default notification center:[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dataIsThere:) name:kMyNetworkNotificationDataIsThere object:nil];
while DataFeeder send the notification at the right time:
[[NSNotificationCenter defaultCenter] postNotificationName:kMyNetworkNotificationDataIsThere object:self];
- make
ViewControllerimplement the delegate methods forNSURLConnectionand handle the response itself (this will require passing it as a parameter toDataFeederconstructor).
回答3:
A nice way to do this would be using blocks. Your doLookup: method could accept a block object, and you can invoke that when the connection finishes.
Since you probably want to be able to perform multiple lookups with different completion blocks, you need to associate the passed in block with the appropriate NSURLConnection.
To do this ou can either use an NSURLConnection subclass with a completionBlock property, or use objective-C associated objects (by using the objc_setAssociatedObject and objc_getAssociatedObject functions) to attach the block to the connection object.
When everything is ready in the connectionDidFinishLoading: method and you've prepared the final response object, you grab the block from the NSURLConnection object and invoke it, passing it the final data.
So you eventually want your client code to look like this:
[feeder doLookup:@"Something" completionBlock:(FetchedData *data, NSError *error) {
if (error) {
// ...
return;
}
// access the returned data
}];
I hope this was enough detail for you.
来源:https://stackoverflow.com/questions/10836106/how-do-you-return-from-an-asynchronous-nsurlconnection-to-the-calling-class