Вопрос по objective-c, ios5 – Как вы возвращаетесь из асинхронного NSURLConnection к вызывающему классу?

4

У меня есть следующий класс, который выполняет HTTP-пост-запрос асинхронно, чтобы избежать проблем в основном потоке пользовательского интерфейса:

@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"];

Итак, вот как я это понимаю:

The doLookup will 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

Как я могу заставить вызывающий контроллер слушать это? Нужно ли реализовать мои собственные методы обратного вызова в ViewController, который будет прослушивать вызов, а затем останавливать спиннер и обновлять пользовательский интерфейс на основе содержимогоmyObj ?

Я надеюсь, что это действительно простой способ, который я упустил из виду ...

Спасибо

Ваш Ответ

3   ответа
4

У вас есть несколько подходов, чтобы получить васViewController уведомление, когда данные есть:

  1. define a delegate protocol between the ViewController and the DataFeeder, so that the latter sends a message to the former in connectionDidFinishLoading:;

  2. use NSNotificationCenter so to decouple DataFeeder and ViewController: ViewController adds itself as an observer to the default notification center:

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

в то время какDataFeeder отправить уведомление в нужное время:

     [[NSNotificationCenter defaultCenter] postNotificationName:kMyNetworkNotificationDataIsThere object:self];
  1. make ViewController implement the delegate methods for NSURLConnection and handle the response itself (this will require passing it as a parameter to DataFeeder constructor).
2

doLookup: Метод может принять блочный объект, и вы можете вызвать его, когда соединение завершится.

Поскольку вы, вероятно, хотите иметь возможность выполнять несколько поисков с разными блоками завершения, вам необходимо связать переданный блок с соответствующим NSURLConnection.

Для этого вы можете использоватьNSURLConnection подкласс сcompletionBlock или использовать объекты, связанные с объектом C (используяobjc_setAssociatedObject а такжеobjc_getAssociatedObject функции), чтобы прикрепить блок к объекту подключения.

Когда все готово вconnectionDidFinishLoading: метод, и вы подготовили конечный объект ответа, вы берете блок изNSURLConnection объект и вызвать его, передавая ему окончательные данные.

Таким образом, вы в конечном итоге хотите, чтобы ваш клиентский код выглядел так:

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

    // access the returned data
}];

Я надеюсь, что это было достаточно подробно для вас.

Error: User Rate Limit Exceeded Jimmy
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.

Error: User Rate Limit Exceeded Jimmy
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceededdelegate_Error: User Rate Limit ExceededViewControllerError: User Rate Limit ExceededDataFeederError: User Rate Limit Exceeded Jimmy

Похожие вопросы