46

AFJSONRequestOperation を使用する関数があり、成功した場合にのみ結果を返したいと考えています。正しい方向に私を向けることができますか?特にブロックと AFNetworking については、まだ少し無知です。

-(id)someFunction{
    __block id data;

    AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request
        success:^(NSURLRequest *request, NSHTTPURLResponse *response, id json){
            data = json;
            return data; // won't work
        }
        failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error){

        }];



    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperation: operation];

    return data; // will return nil since the block doesn't "lock" the app.
}
4

6 に答える 6

56

[operation waitUntilFinished]操作が完了するまでメイン スレッドの実行をブロックするには、操作キューに追加された後に行うことができます。この場合、returnブロック内に は必要ありません。変数を設定する__blockだけで十分です。

そうは言っても、非同期操作を同期メソッドに強制することは強くお勧めしません。頭を理解するのは難しい場合もありますが、これを非同期に構成できる方法があれば、ほぼ間違いなくそれが最善の方法です。

于 2011-11-01T17:04:55.137 に答える
12

この問題を解決するためにセマフォを使用しています。このコードは、 から継承した独自のクラスに実装されていAFHTTPClientます。

__block id result = nil;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSURLRequest *req = [self requestWithMethod:@"GET"
                                       path:@"someURL"
                                 parameters:nil];
AFHTTPRequestOperation *reqOp = [self HTTPRequestOperationWithRequest:req
                                                              success:^(AFHTTPRequestOperation *operation, id responseObject) {
                                                                  result = responseObject;                                                                          
                                                                  dispatch_semaphore_signal(semaphore);
                                                              }
                                                              failure:^(AFHTTPRequestOperation *operation, NSError *error) {
                                                                  dispatch_semaphore_signal(semaphore);
                                                              }];
reqOp.failureCallbackQueue = queue;
reqOp.successCallbackQueue = queue;
[self enqueueHTTPRequestOperation:reqOp];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_release(semaphore);
return result;
于 2013-10-02T09:13:29.127 に答える
11

AFNetworking (または一般的なブロック) で同期メソッドを作成しないことをお勧めします。別のメソッドを作成し、成功ブロックの json データを引数として使用することをお勧めします。

- (void)methodUsingJsonFromSuccessBlock:(id)json {
    // use the json
    NSLog(@"json from the block : %@", json); 
}

- (void)someFunction {
    AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request
        success:^(NSURLRequest *request, NSHTTPURLResponse *response, id json){
            // use the json not as return data, but pass it along to another method as an argument
            [self methodUsingJsonFromSuccessBlock:json];
        }
        failure:nil];

    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperation: operation];
}
于 2011-11-01T17:20:23.173 に答える
6

AFNetworking の AFClient の一部の機能は引き続き同期的に使用できることに注意してください。つまり、Authorization ヘッダーやマルチパート アップロードなどの優れた機能を引き続き使用できます。

例えば:

NSURLRequest *request = [self.client requestWithMethod: @"GET"
                                                  path: @"endpoint"
                                            parameters: @{}];
NSHTTPURLResponse *response = nil;
NSError *error = nil;

NSData *responseData = [NSURLConnection sendSynchronousRequest: request
                                             returningResponse: &response
                                                         error: &error];

response.statusCodeこのメソッドは HTTP 失敗コードをエラーと見なさないため、この場合は忘れずに確認してください。

于 2013-09-20T17:27:15.527 に答える
1

@Kasikの回答を拡張/更新するには。セマフォを使用して、AFNetworking でカテゴリを作成できます。

@implementation AFHTTPSessionManager (AFNetworking)

- (id)sendSynchronousRequestWithBaseURLAsString:(NSString * _Nonnull)baseURL pathToData:(NSString * _Nonnull)path parameters:(NSDictionary * _Nullable)params {
    __block id result = nil;
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    AFHTTPSessionManager *session = [[AFHTTPSessionManager alloc]initWithBaseURL:[NSURL URLWithString:baseURL]];
    [session GET:path parameters:params progress:nil success:^(NSURLSessionDataTask *task, id responseObject) {
        result = responseObject;
        dispatch_semaphore_signal(semaphore);
    } failure:^(NSURLSessionDataTask *task, NSError *error) {
        dispatch_semaphore_signal(semaphore);
    }];
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    return result;
 }

@end

別の AFNetwork リクエストの完了ブロック内で同期ブロックを呼び出す場合は、必ずcompletionQueueプロパティを変更してください。変更しないと、同期ブロックは完了時に既にメイン キューにあるときにメイン キューを呼び出し、アプリケーションをクラッシュさせます。

+ (void)someRequest:(void (^)(id response))completion {
    AFHTTPSessionManager *session = [[AFHTTPSessionManager alloc]initWithBaseURL:[NSURL URLWithString:@""] sessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
    dispatch_queue_t queue = dispatch_queue_create("name", 0);
    session.completionQueue = queue;
    [session GET:@"path/to/resource" parameters:nil progress:nil success:^(NSURLSessionDataTask *task, id responseObject) {
     NSDictionary *data = [session sendSynchronousRequestWithBaseURLAsString:@"" pathToData:@"" parameters:nil ];
      dispatch_async(dispatch_get_main_queue(), ^{
          completion (myDict);
      });
} failure:^(NSURLSessionDataTask *task, NSError *error) {
    dispatch_async(dispatch_get_main_queue(), ^{
        completion (error);
    });
}];
于 2016-03-15T20:36:09.717 に答える