NSOperationQueue и ASIHTTPRequest

Я пишу тестовые примеры для класса-оболочки, написанного вокруг ASIHTTPRequest. По причинам, которые я не могу определить, мои тестовые примеры завершились с ошибкой до завершения ASIHTTPRequest.

Вот как работает программа.

  1. Начнем с моего тестового примера.
  2. Запустить мой объект http-движка, дать ему указание создать новый список
  3. Создайте новый объект ASIHTTPRequest и настройте его.
  4. Добавьте запрос в очередь операций.
  5. Подождите, пока эта очередь не станет пустой
  6. Проверьте, вызывались ли мои методы делегата, и провалите тест, если нет.

Сейчас большую часть времени все работает нормально и тест проходит успешно, но иногда он терпит неудачу, потому что мои методы делегата были вызваны ПОСЛЕ того, как очередь операций вернула управление моему методу ожидания.

Прецедент

// Set my flags to 'NO'
- (void)setUp {
    requestDidFinish = NO;
    requestDidFail = NO;
}

- (void)testCreateList {
    NSString *testList = @"{\"title\": \"This is a list\"}";

    JKEngine *engine = [[JKEngine alloc] initWithDelegate:self];
    NSString *requestIdentifier = [engine createList:jsonString];

    [self waitUntilEngineDone:engine];
    NSString *responseString = responseString_;
    [engine release];

    GHAssertNotNil(requestIdentifier, nil);
    GHAssertTrue(requestDidFinish, nil);
    GHAssertTrue([responseString hasPrefix:@"{\"CreateOrEditListResult\""], nil);

}

// Puts the test into a holding pattern until the http request is done
- (void)waitUntilEngineDone:(JKEngine *)engine {
    [engine waitUntilFinishedRunning]; 
}

// The delegate method called on successful completion
- (void)requestFinished:(NSString *)requestIdentifier withResponse:(NSString *)response {
    NSLog(@"request did finish");
    requestDidFinish = YES;
    responseIdentifier_ = [requestIdentifier retain];
    responseString_ = [response retain];
}

Код двигателя

- (NSString *)createList:(NSString *)list {
    ASIHTTPRequest *request = [[ASIHTTPRequest alloc] initWithURL:[NSURL URLWithString:url]];
    [request addRequestHeader:@"Content-Type" value:kContentType];
    [request setRequestMethod:kPOST];
    request.delegate = self;

    [request appendPostData:[list dataUsingEncoding:NSUTF8StringEncoding]];

    NSString *requestIdentifier = [NSString stringWithNewUUID];

    [operationQueue_ addOperation:request];
    [operationDictionary_ setObject:request forKey:requestIdentifier];

    return requestIdentifier;
}

// This is the ASIHTTPRequest delegate method that's called on success
//   but it sometimes isn't called until AFTER the operationQueue finishes running
- (void)requestFinished:(ASIHTTPRequest *)request {
    DLog([request responseString]);

    BOOL canNotifiyDelegate = [self.delegate respondsToSelector:@selector(requestFinished:withResponse:)];
    if (canNotifiyDelegate) {
        NSArray *keyArray = [operationDictionary_ allKeysForObject:request];
        NSString *requestIdentifier = [keyArray objectAtIndex:0];
        [operationDictionary_ removeObjectForKey:requestIdentifier];

        if ([keyArray count] != 1) {
            ALog(@"It looks like a request was added to the operation dictionary multiple times. There's a bug somewhere.", nil);
        }

        [self.delegate requestFinished:requestIdentifier withResponse:[request responseString]];
    }
}

- (void)waitUntilFinishedRunning {
    [operationQueue_ waitUntilAllOperationsAreFinished];
}

person kubi    schedule 24.07.2010    source источник


Ответы (2)


Так работает ASIHTTPRequest. Методы делегата вызываются в основном потоке, а вызовы делегатов не блокируют поток запроса, поэтому вполне возможно, что ваши делегаты будут вызываться после завершения очереди.

person pokeb    schedule 25.07.2010
comment
Собственно, я ожидаю, что это сработает именно так. Что вызывает у меня цикл, так это то, что иногда методы делегата вызываются ПОСЛЕ завершения очереди. Если это помогает, все тесты выполняются в основном потоке. - person kubi; 25.07.2010
comment
Извините, у меня было это наоборот, то, что вы описали, - это то, что происходит. Вы знаете надежный способ проверить, завершен ли запрос? Похоже, единственный способ сделать это - опросить мой текущий объект и определить, были ли вызваны requestFinished или requestFailed. - person kubi; 25.07.2010

ASIHTTPRequest вызывает методы делегата в основном потоке, по умолчанию GH-Unit выполняет свои тесты в фоновом потоке. Я все еще не совсем понимаю, что именно происходит, но принудительный запуск моих сетевых тестов в основном потоке устранил проблему.

Я реализовал следующий метод в своем классе тестирования сети.

- (BOOL)shouldRunOnMainThread { 
    return YES;
}
person kubi    schedule 26.07.2010