2012-05-31 47 views
4

我有下面的類,這使得HTTP POST請求異步地避免在主UI線程上的問題:如何從異步NSURLConnection返回到調用類?

@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. 一旦數據加載完成後,我將建立從響應數據的對象,我會發回給呼叫控制器

我怎麼讓呼叫控制器聽這個?我是否需要在ViewController中實現自己的回調方法,該方法將偵聽調用,然後停止微調並根據myObj的內容更新UI?

我希望那裏有,我已經忽略了一個非常簡單的方法...

感謝

回答

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方法。

+0

如何設置'delegate_',更重要的是,我在哪裏設置?當我在使用之前創建並初始化DataFeeder時,推測在'ViewController'中? – Jimmy

+0

編輯我的答案,檢查出來。別的,只要問。 –

+0

完美,幫助我!不像Android中的異步任務一樣優雅,但嘿嘿。 – Jimmy

4

你有幾種方法可以讓你ViewController通知當數據有:

  1. 定義ViewControllerDataFeeder之間的委託協議,讓後者在connectionDidFinishLoading:將消息發送到前者;

  2. 使用NSNotificationCenter所以去耦DataFeederViewControllerViewController增加了自身作爲觀察員默認的通知中心:

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dataIsThere:) name:kMyNetworkNotificationDataIsThere object:nil]; 
    

DataFeeder發送通知在正確的時間:

 [[NSNotificationCenter defaultCenter] postNotificationName:kMyNetworkNotificationDataIsThere object:self]; 
  1. 使ViewController實現NSURLConnection的委託方法並處理響應本身(這需要將它作爲參數傳遞給DataFeeder構造函數)。
2

一個很好的方法來做到這一點將使用塊。您的doLookup:方法可以接受塊對象,並且可以在連接完成時調用它。

由於您可能希望能夠使用不同的完成塊執行多個查找,因此您需要將傳入的塊與相應的NSURLConnection關聯。

爲此OU既可以使用一個子類NSURLConnectioncompletionBlock屬性,或使用客觀-C相關聯的對象(通過使用objc_setAssociatedObjectobjc_getAssociatedObject函數)到塊附加到連接對象。

當方法中的一切都準備就緒並且您已準備好最終響應對象時,您從NSURLConnection對象中抓取塊並調用它,並將最終數據傳遞給它。

所以,你最終想要你的客戶端代碼如下所示:

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

    // access the returned data 
}]; 

我希望這是足夠的細節你。

+0

+1爲答案,但不幸的是,我相對比較新的iOS選擇了(稍微容易理解)委託/協議選項。謝謝 – Jimmy