4

I am converting my app routines from ASIHTTP to AFNetworking due to the unfortunate discontinuation of work on that project ... and what I found out later to be the much better and smaller codebase of AFNetworking.

I am finding several issues. My code for ASIHTTPRequest is built as a method. This method takes a few parameters and posts the parameters to a url ... returning the resulting data. This data is always text, but in the interests of making a generic method, may sometimes be json, sometimes XML or sometimes HTML. Thus I built this method as a standalone generic URL downloader.

My issue is that when the routine is called I have to wait for a response. I know all the "synchronous is bad" arguments out there...and I don't do it a lot... but for some methods I want synchronous.

So, here is my question. My simplified ASIHTTP code is below, followed by the only way i could think of coding this in AFNetworking. The issue I have is that the AFNetworking sometimes does not for the response before returning from the method. The hint that @mattt gave of [operation waitUntilFinished] totally fails to hold the thread until the completion block is called... and my other method of [queue waitUntilAllOperationsAreFinished] does not necessarily always work either (and does NOT result in triggering the error portion of the [operation hasAcceptableStatusCode] clause). So, if anyone can help, WITHOUT The ever-present 'design it asynchronously', please do.

ASIHTTP version:

- (NSString *) queryChatSystem:(NSMutableDictionary *) theDict
{
    NSString *response = [NSString stringWithString:@""];
    NSString *theUrlString = [NSString stringWithFormat:@"%@%@",kDataDomain,kPathToChatScript];

    ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString:theUrlString]];
    for (id key in theDict)
    {
        [request setPostValue:[theDict objectForKey:key] forKey:key];
    }

    [request setNumberOfTimesToRetryOnTimeout:3];
    [request setAllowCompressedResponse:YES];
    [request startSynchronous];

    NSError *error = [request error];
    if (! error)
    {
        response = [request responseString];
    }

    return response;
}

AFNetworking version

- (NSString *) af_queryChatSystem:(NSMutableDictionary *) theDict
{
    NSMutableDictionary *theParams = [NSMutableDictionary dictionaryWithCapacity:1];

    for (id key in theDict)
    {
        [theParams setObject:[theDict objectForKey:key] forKey:key];
    }


    AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:kDataDomain]];

    NSMutableURLRequest *theRequest = [httpClient requestWithMethod:@"POST" path:[NSString stringWithFormat:@"/%@",kPathToChatScript] parameters:theParams];


    __block NSString *responseString = [NSString stringWithString:@""];

    AFHTTPRequestOperation *operation = [[[AFHTTPRequestOperation alloc] initWithRequest:theRequest] autorelease];

    operation.completionBlock = ^ {
        if ([operation hasAcceptableStatusCode]) {
            responseString = [operation responseString];
            NSLog(@"hasAcceptableStatusCode: %@",responseString);
        }
        else
        {
            NSLog(@"[Error]: (%@ %@) %@", [operation.request HTTPMethod], [[operation.request URL] relativePath], operation.error);
        }
    };

    NSOperationQueue *queue = [[[NSOperationQueue alloc] init] autorelease];
    [queue addOperation:operation];
    [queue waitUntilAllOperationsAreFinished];
    [httpClient release];


    return responseString;

}

Thanks very much for any ideas.

4

3 に答える 3

3
- (void)af_queryChatSystem:(NSMutableDictionary *) theDict block:(void (^)(NSString *string))block {
...
}

ここで、completionBlock 内で次のことを行います。

block(operation.responseString);

ブロックは操作のデリゲートとして機能します。削除する

-waitUntilAllOperationsAreFinished

return responseString

これを次のように呼び出します。

[YourInstance af_queryChatSystem:Dict block:^(NSString *string) {
    // use string here
}];

それが役に立てば幸い。AFNetworking の iOS の例を参照できます。

于 2011-11-17T20:52:21.770 に答える
0

私の解決策は、コールバックが処理されるまで現在のスレッドの runloop を手動で実行することです。

これが私のコードです。

- (void)testRequest
{
    MyHTTPClient* api = [MyHTTPClient sharedInstance]; // subclass of AFHTTPClient
    NSDictionary* parameters = [NSDictionary dictionary]; // add query parameters to this dict.
    __block int status = 0;
    AFJSONRequestOperation* request = [api getPath:@"path/to/test"
                                        parameters:parameters
                                           success:^(AFHTTPRequestOperation *operation, id responseObject) {
                                               // success code
                                               status = 1;
                                               NSLog(@"succeeded");
                                           } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
                                               // failure
                                               status = 2;
                                               NSLog(@"failed");
                                           }];
    [api enqueueHTTPRequestOperation:request];
    [api.operationQueue waitUntilAllOperationsAreFinished];

    while (status == 0)
    {

        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
                                 beforeDate:[NSDate date]];
    }

    STAssertEquals(status, 1, @"success block was executed");
}
于 2013-01-21T02:50:09.153 に答える
0

NSURLConnectionこの機会を利用して、さらに別のサードパーティ API を採用するのではなく、Apple 独自の に変換することを強くお勧めします。そうすれば、それが中止されることはないと確信できます。動作させるために必要な追加作業は最小限であることがわかりましたが、はるかに堅牢でエラーが発生しにくいことがわかりました。

于 2011-11-17T20:31:05.477 に答える