При использовании делегатов нужен лучший способ последовательной обработки - PullRequest
3 голосов
/ 13 мая 2010

У меня есть класс WebServiceCaller, который использует NSURLConnection для выполнения асинхронных вызовов веб-службы. Класс предоставляет свойство делегата и, когда вызов веб-службы завершен, вызывает делегата метод webServiceDoneWithXXX.

Существует несколько методов веб-службы, которые можно вызвать, например, GetSummary и GetList.

Классы, которые используют WebServiceCaller, изначально нуждаются как в сводке, так и в списке, поэтому они пишутся так:

-(void)getAllData {
    [webServiceCaller getSummary];
}
-(void)webServiceDoneWithGetSummary {
    [webServiceCaller getList];
}
-(void)webServiceDoneWithGetList {
    ...
}

Это работает, но есть как минимум две проблемы:

  1. Вызовы распределены по делегату. методы, поэтому трудно увидеть последовательность с первого взгляда, но больше важно, что это трудно контролировать или изменить последовательность.
  2. Иногда я хочу вызвать только GetSummary, а не GetList, поэтому я тогда придется использовать уродливый уровень класса переменная состояния, которая сообщает webServiceDoneWithGetSummary ли вызывать GetList или нет.

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

Есть ли лучший способ справиться с этим и получать асинхронные вызовы?

Обновление на основе ответа Мэтта Лонга:

Используя уведомления вместо делегата, похоже, что я могу решить проблему # 2, установив другой селектор в зависимости от того, хочу ли я полную последовательность (GetSummary + GetList) или просто GetSummary. Оба обозревателя будут по-прежнему использовать одно и то же имя уведомления при вызове GetSummary. Мне пришлось бы написать два отдельных метода для обработки GetSummaryDone вместо использования одного метода делегата (где мне понадобилась бы некоторая переменная уровня класса, чтобы указать, следует ли затем вызывать GetList).

-(void)getAllData {
    [[NSNotificationCenter defaultCenter] addObserver:self
             selector:@selector(getSummaryDoneAndCallGetList:) 
                 name:kGetSummaryDidFinish object:nil];
    [webServiceCaller getSummary];
}
-(void)getSummaryDoneAndCallGetList {
    [NSNotificationCenter removeObserver]
    //process summary data

    [[NSNotificationCenter defaultCenter] addObserver:self
             selector:@selector(getListDone:) 
                 name:kGetListDidFinish object:nil];
    [webServiceCaller getList];
}
-(void)getListDone {
    [NSNotificationCenter removeObserver]
    //process list data 
}


-(void)getJustSummaryData {
    [[NSNotificationCenter defaultCenter] addObserver:self
             selector:@selector(getJustSummaryDone:)     //different selector but
                 name:kGetSummaryDidFinish object:nil];  //same notification name
    [webServiceCaller getSummary];
}
-(void)getJustSummaryDone {
    [NSNotificationCenter removeObserver]
    //process summary data
}

На самом деле я еще не пробовал это. Это кажется лучше, чем иметь переменные состояния и операторы if-then, но вам нужно написать больше методов. Я до сих пор не вижу решения проблемы 1.

1 Ответ

1 голос
/ 13 мая 2010

Это правда, что получение результатов ваших вызовов веб-сервисов является (и должно быть) асинхронным, однако вы хотите, чтобы ваши вызовы происходили в определенной последовательности. Один из способов - подождать, пока закончится первый, прежде чем вызывать второй, и опубликовать уведомление, когда первый будет завершен. Итак, в -connectionDidFinishLoading вашего первого запроса (я полагаю, в вашем webServiceCaller) опубликуйте уведомление на вашем контроллере, сообщающее, что первый запрос завершился успешно. Что-то вроде:

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    [connection release];
    // receivedData is an NSMutableData object we've been appending
    // to in the -didReceiveData delegate. We'll pass it in the
    // notification so we can hand the data off to the next request.
    [[NSNotificationCenter defaultCenter] 
           postNotificationName:kGetSummaryDidFinish object:receivedData];
}

Затем, вернувшись в свой контроллер, зарегистрируйтесь для этого уведомления:

- (void)viewDidLoad;
{
    [super viewDidLoad];
    [[NSNotificationCenter defaultCenter] addObserver:self
             selector:@selector(getSummaryDidFinish:) 
                 name:kGetSummaryDidFinish object:nil];
}

- (void) getSummaryDidFinish:(NSNotification*)notification;
{
    // If you needed the downloaded data, we passed it in
    NSData *data = [notification object];

    // Decide here if you want to call getList or not.
    if (someConditionOfDataObjectIsTrue)
        [webServiceCaller getList];
}

Если у вас много вызовов, которые ждут друг от друга, как это, это может стать довольно запутанным и сложным в обслуживании, поэтому, возможно, вам стоит рассмотреть шаблон проектирования (не тот, который приходит на ум в данный момент). Тем не менее, этот метод работал очень хорошо для меня. Уведомления помогают сделать это более понятным, поскольку вы можете вызывать все свои запросы от вашего контроллера, когда вы получаете уведомление, соответствующее некоторым критериям.

Надеюсь, что это имеет смысл.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...