你如何从异步NSURLConnection返回到调用类?

时间:2012-05-31 14:58:19

标签: objective-c ios5 xcode4

我有以下类异步发出HTTP post请求以避免主UI线程出现问题:

@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

我正在使用以下代码行从我的ViewController调用上述内容:

MyObject * myObj = [feeder doLookup:@"SomeStaticStringForNow"];

所以,这就是我理解的方式:

  1. doLookup将在异步上执行请求 连接。
  2. 数据完全加载后,将调用connectionDidFinishLoading
  3. 数据加载完成后,我将从响应数据中建立一个对象,我将发送回调用控制器
  4. 如何让呼叫控制器听取此消息?我是否需要在ViewController中实现我自己的回调方法,该方法将监听调用,然后停止微调器并根据myObj的内容更新UI?

    我希望这是一个我忽略的简单方法......

    由于

3 个答案:

答案 0 :(得分:6)

是的,您应该使用委托模式实现回调。也就是说,在我看来,这是最简单,最标准的方法。还有其他方法,您可以在其他回复中看到。

DataFeeder.h文件中:

@protocol DataFeederDelegate
 - (void)dataReady:(NSData*)data;
@end

@interface DataFeeder : NSObject {
 id delegate_;
}
- (id)initWithDelegate:(id<DataFeederDelegate>)delegate;
@end

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

您将实例化一个DataFeeder对象:

DataFeeder *dataFeeder = [[DataFeeder alloc] initWithDelegate:self];

当然,调用视图控制器必须实现DataFeederDelegate方法。

答案 1 :(得分:4)

有数种方法可以在数据出现时通知ViewController

  1. ViewControllerDataFeeder之间定义委托协议,以便后者在connectionDidFinishLoading:中向前者发送消息;

  2. 使用NSNotificationCenter,以便将DataFeederViewController分离:ViewController将自己添加为默认通知中心的观察者:

     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dataIsThere:) name:kMyNetworkNotificationDataIsThere object:nil];
    
  3. DataFeeder在正确的时间发送通知:

         [[NSNotificationCenter defaultCenter] postNotificationName:kMyNetworkNotificationDataIsThere object:self];
    
    1. make ViewController实现NSURLConnection的委托方法并自行处理响应(这需要将其作为参数传递给DataFeeder构造函数)。

答案 2 :(得分:2)

一个很好的方法是使用块。您的doLookup:方法可以接受块对象,您可以在连接完成时调用它。

由于您可能希望能够使用不同的完成块执行多次查找,因此需要将传入的块与相应的NSURLConnection相关联。

要执行此操作,您可以使用带有NSURLConnection属性的completionBlock子类,也可以使用Objective-C关联对象(使用objc_setAssociatedObjectobjc_getAssociatedObject函数)将块附加到连接对象。

connectionDidFinishLoading:方法中的所有内容都准备好并且您已准备好最终响应对象时,您从NSURLConnection对象中获取块并调用它,并将最终数据传递给它。

所以你最终希望你的客户端代码看起来像这样:

[feeder doLookup:@"Something" completionBlock:(FetchedData *data, NSError *error) {
    if (error) {
        // ...
        return;
    }

    // access the returned data
}];

我希望这对你来说足够详细。