2013-12-09 19 views
8

Tôi biết có another similar question, nhưng nó dành cho phiên bản AFNetworking cũ hơn và không thực sự trả lời.AFNetworking-2 waitUntilFinished not working

Tôi đã đoạn mã sau:

AFHTTPRequestOperationManager* manager = [AFHTTPRequestOperationManager manager]; 
manager.securityPolicy.allowInvalidCertificates = YES; 
manager.requestSerializer = [AFJSONRequestSerializer serializer]; 
[manager.requestSerializer setAuthorizationHeaderFieldWithUsername: currentUser() password: currentPassword()]; 
__block NSDictionary* response = nil; 
AFHTTPRequestOperation* operation = [manager 
    GET: @"https://10.20.30.40:8765/foobar" 
    parameters: [NSDictionary dictionary] 
    success:^(AFHTTPRequestOperation* operation, id responseObject){ 
     response = responseObject; 
     NSLog(@"response (block): %@", response); 
    } 
    failure:^(AFHTTPRequestOperation* operation, NSError* error){ 
     NSLog(@"Error: %@", error);} 
]; 
[operation waitUntilFinished]; 
NSLog(@"response: %@", response); 
... 

Nếu tôi chạy này, những gì tôi sẽ thấy trong nhật ký của tôi là:

2013-12-09 09:26:20.105 myValve[409:60b] response: (null) 
2013-12-09 09:26:20.202 myValve[409:60b] response (block): { 
    F00005 = ""; 
    F00008 = ""; 
    F00013 = ""; 
} 

Các NSLog đó là sau các waitUntilFinished cháy đầu tiên . Tôi hy vọng nó sẽ cháy thứ hai. Tôi đang thiếu gì?

Trả lời

32

Một vài suy nghĩ:

  1. Vấn đề là waitUntilFinished sẽ chờ cho mạng lưới hoạt động cốt lõi để hoàn thành, nhưng nó sẽ không chờ cho success hoặc failure khối hoàn thành. Nếu bạn muốn chờ đợi cho các khối hoàn thành, bạn có thể sử dụng một semaphore:

    __block NSDictionary* response = nil; 
    
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); 
    
    manager.completionQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 
    
    AFHTTPRequestOperation* operation = [manager GET: @"https://10.20.30.40:8765/foobar" 
                 parameters: [NSDictionary dictionary] 
                 success:^(AFHTTPRequestOperation* operation, id responseObject){ 
                  response = responseObject; 
                  NSLog(@"response (block): %@", response); 
                  dispatch_semaphore_signal(semaphore); 
                 } 
                 failure:^(AFHTTPRequestOperation* operation, NSError* error){ 
                  NSLog(@"Error: %@", error); 
                  dispatch_semaphore_signal(semaphore); 
                 }]; 
    
    NSLog(@"waiting"); 
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); 
    // [operation waitUntilFinished]; 
    NSLog(@"response: %@", response); 
    

    Bạn có thể, cách khác, quấn này trong đồng thời riêng NSOperation lớp con của bạn, đăng isFinished trong AFHTTPRequestOperation khối hoàn, loại bỏ sự semaphore trong quá trình.

    Lưu ý, đảm bảo chỉ định completionQueue nếu làm ẩn dụ trên hàng đợi chính vì, khi không có điều đó, AFNetworking sẽ mặc định gửi trình xử lý hoàn thành tới hàng đợi chính và bạn có thể bế tắc. Là một sang một bên, bạn không bao giờ nên chặn hàng đợi chính (UX kém, ứng dụng của bạn có thể bị giết bởi quá trình giám sát, vv), vì vậy nếu bạn làm điều này từ hàng đợi chính, tôi sẽ không khuyến khích sử dụng waitUntilFinished hoặc semaphore. Nó tốt hơn để chỉ khởi bất cứ điều gì bạn cần từ bên trong khối hoàn thành, cho phép hàng đợi chính tiếp tục thực hiện trong khi mạng lưới hoạt động không đồng bộ này đang tiến hành, ví dụ:

    [activityIndicatorView startAnimating]; 
    
    AFHTTPRequestOperation* operation = [manager GET: @"https://10.20.30.40:8765/foobar" 
                 parameters: [NSDictionary dictionary] 
                 success:^(AFHTTPRequestOperation* operation, id responseObject){ 
    
                  // do whatever you want with `responseObject` here 
    
                  // now update the UI, e.g.: 
    
                  [activityIndicatorView stopAnimating]; 
                  [self.tableView reloadData]; 
                 } 
                 failure:^(AFHTTPRequestOperation* operation, NSError* error){ 
    
                  // put your error handling here 
    
                  // now update the UI, e.g.: 
    
                  [activityIndicatorView stopAnimating]; 
                 }]; 
    
    // NSLog(@"waiting"); 
    // dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); 
    // // [operation waitUntilFinished]; 
    // NSLog(@"response: %@", response); 
    

Có vẻ như bạn muốn hãy để mô hình của bạn cho phép giao diện người dùng thực hiện bất kỳ cập nhật cần thiết nào khi đối tượng mô hình được thực hiện thực hiện cập nhật của nó. Vì vậy, bạn có thể sử dụng tham số khối của riêng mình để bộ điều khiển xem có thể cho đối tượng mô hình biết phải làm gì khi thực hiện (thay vì sử dụng waitUntilFinished hoặc semaphore để làm cho hoạt động mạng chặn hàng đợi chính). Ví dụ, chúng ta hãy giả định mô hình của bạn đã có một số phương pháp như thế này:

- (void)updateModelWithSuccess:(void (^)(void))success failure:(void (^)(NSError *error))failure 
{ 
    AFHTTPRequestOperationManager* manager = [AFHTTPRequestOperationManager manager]; 
    manager.securityPolicy.allowInvalidCertificates = YES; 
    manager.requestSerializer = [AFJSONRequestSerializer serializer]; 
    [manager.requestSerializer setAuthorizationHeaderFieldWithUsername: currentUser() password: currentPassword()]; 
    AFHTTPRequestOperation* operation = [manager GET: @"https://10.20.30.40:8765/foobar" 
              parameters: [NSDictionary dictionary] 
              success:^(AFHTTPRequestOperation* operation, id responseObject){ 

               // do your model update here 

               // then call the success block passed to this method (if any), 
               // for example to update the UI 

               if (success) 
                success(); 
              } 
              failure:^(AFHTTPRequestOperation* operation, NSError* error){ 
               NSLog(@"Error: %@", error); 

               // if caller provided a failure block, call that 

               if (failure) 
                failure(error); 
              }]; 
} 

Sau đó điều khiển xem bạn có thể làm điều gì đó như:

[modelObject updateModelWithSuccess:^{ 
    // specify UI updates to perform upon success, e.g. 
    // stop activity indicator view, reload table, etc. 
} failure:^(NSError *error){ 
    // specify any UI updates to perform upon failure 
}] 

Bottom line, mã của bạn có thể sử dụng cùng một kiểu khối hoàn thành mà AFNetworking sử dụng. Nếu bạn muốn mô hình truyền lại thông tin, bạn có thể thêm các tham số bổ sung vào các khối hoàn thành, nhưng tôi cho rằng mô hình trên minh họa ý tưởng cơ bản.

+4

Thật không may, tùy chọn # 1 chỉ bị treo mọi thứ hoàn toàn. Cả khối không bao giờ cháy. Không chắc chắn tại sao đặt nó ở đó, nhưng nó có. –

+0

Tôi hiểu điều "treo giao diện người dùng xấu".Những gì tôi đấu tranh trong trường hợp này, đây là các truy vấn REST để cập nhật các mô hình cục bộ. Vì vậy, tôi muốn đặt công cụ AF này trong các lớp mô hình để thực hiện và thực hiện cập nhật. Vì vậy, sau đó tôi sẽ phải tăng thêm bất kỳ phương pháp nào trong số các phương thức này với andWhenYourDone: các khối, để tôi có thể kích hoạt cập nhật giao diện người dùng khi thích hợp. –

+0

@TravisGriggs Sau đó, chỉ cần cung cấp các tham số khối phương thức 'updateModel' của mô hình (không giống như các khối' success' và 'failure' mà AFNetworking, chính nó, sử dụng). Bằng cách đó, trình điều khiển chế độ xem có thể nói, có hiệu quả, "cập nhật mô hình và khi hoàn tất, hãy thực hiện x, y và z". Xem câu trả lời đã sửa đổi. – Rob