2012-02-23 25 views
7

Tôi cần thực hiện một số cuộc gọi POST tới máy chủ của mình, nhưng tôi không cần chặn luồng chính. Theo tôi hiểu, NSMutableURLRequestNSURLConnection không phải là chủ đề an toàn, vì vậy tốt nhất bạn nên sử dụng phương pháp async là NSURLConnection.Mục tiêu-C: Không đồng bộ/Bối cảnh POST mà không sử dụng phương pháp đại biểu?

Câu hỏi của tôi về điều này là, làm thế nào tôi có thể gói nó lên một cách độc đáo vào một phương pháp, thay vì phải sử dụng phương pháp đại biểu? Tôi muốn làm:

NSData *returnedData = [Utility postDataToURL:@"some string of data"]; 

Đây là cách dễ dàng được thực hiện với các phương pháp sau đây:

[NSURLConnection sendSynchronousRequest:serviceRequest returningResponse:&serviceResponse error:&serviceError]; 

Nó là như vậy thoải mái giữ tất cả mọi thứ trong vòng một phương pháp, sau đó chỉ có dữ liệu của tôi trở về từ nó !

Có phương pháp chặn nào cho việc này không? Nó trở thành một vấn đề khi tôi cần viết các phương thức cho khoảng 50 cuộc gọi khác nhau và mỗi người cần sử dụng cùng một phương thức ủy nhiệm. Tôi đang đi về điều này một cách sai lầm?

Điều này sẽ chỉ cần cho iOS5.

+1

Bạn nói bạn không muốn chặn chủ đề chính, nhưng những gì bạn nói rằng bạn muốn làm là rất rõ ràng đồng bộ, tức là nó sẽ chặn các chủ đề chính (hoặc ít nhất, các chủ đề bạn đang gọi nó từ). –

+0

Đó là ví dụ về mức độ dễ dàng tôi muốn có thể trả lại dữ liệu. –

Trả lời

9

iOS 5 thêm sendAsynchronousRequest:queue:completionHandler: làm những gì tôi nghĩ bạn muốn. Tôi đã có mã của tôi thiết lập để sử dụng nếu có, nhưng để rơi trở lại thực hiện một lấy đồng bộ trên một hàng đợi GCD nền và nhảy lên sợi chính với kết quả nếu nó không. Sau này sẽ ít năng lượng hiệu quả hơn nhưng nó chỉ để duy trì hỗ trợ di sản.

if([NSURLConnection respondsToSelector:@selector(sendAsynchronousRequest:queue:completionHandler:)]) 
{ 
    // we can use the iOS 5 path, so issue the asynchronous request 
    // and then just do whatever we want to do 
    [NSURLConnection sendAsynchronousRequest:request 
     queue:[NSOperationQueue mainQueue] 
     completionHandler: 
     ^(NSURLResponse *response, NSData *data, NSError *error) 
     { 
      [self didLoadData:data]; 
     }]; 
} 
else 
{ 
    // fine, we'll have to do a power inefficient iOS 4 implementation; 
    // hop onto the global dispatch queue... 
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), 
    ^{ 
     // ... perform a blocking, synchronous URL retrieval ... 
     NSError *error = nil; 
     NSURLResponse *urlResponse = nil; 
     NSData *responseData = 
      [NSURLConnection sendSynchronousRequest:request returningResponse:&urlResponse error:&error]; 

     // ... and hop back onto the main queue to handle the result 
     dispatch_async(dispatch_get_main_queue(), 
     ^{ 
      [self didLoadData:responseData]; 
     }); 
    }); 
} 

Trong mã sản xuất bạn muốn thực sự kiểm tra error s và mã đáp ứng HTTP (như là một phản ứng máy chủ 404 có lẽ cũng giống như nhiều lỗi từ quan điểm của bạn như là một thất bại kết nối), rõ ràng.

+0

Tôi đang tạo ứng dụng iOS 5 để ứng dụng này hoạt động hoàn hảo! Cảm ơn bạn! –

+0

Tốt nhất là đặt hàng đợi thành [NSOperationQueue mainQueue] thay vì sử dụng tên riêng của tôi? –

+0

Điều đó tùy thuộc vào bạn. Tôi đã sử dụng 'mainQueue' bởi vì tôi thực hiện một số công cụ UIKit trong trình xử lý hoàn thành - hàng đợi bạn đang xác định là nơi bạn muốn trình xử lý hoàn thành xuất hiện, vì vậy nếu bạn muốn điều đó xảy ra ở nền sau đó chuyển vào hàng đợi thay thế của bạn. – Tommy

1

iOS 5.0 > bạn có thể sử dụng giao diện phương thức sendAsynchronousRequest tại NSURLConnection Class và sử dụng khối. Nếu bạn muốn hỗ trợ iOS 4.0 > thì bạn phải viết một trong các URL tải không đồng bộ dựa trên khối của riêng bạn, điều này khá dễ viết. Bạn nên tắt bằng cách sử dụng MKNetworkKit.

nhưng tôi không phải chặn chuỗi chính. Theo tôi hiểu, NSMutableURLRequest và NSURLConnection không phải là luồng an toàn, vì vậy tốt nhất là sử dụng phương pháp async của NSURLConnection.

Bạn không muốn thực hiện Kết nối mạng đồng bộ, nó chặn chuỗi cho dù nó được gọi từ đâu (thậm chí còn tệ hơn nếu chủ đề chính của nó). Bạn có thể thực hiện kết nối mạng không đồng bộ trên chủ đề chính. Nếu bạn muốn gọi NSURLConnection trên chuỗi không phải chính thì phải tạo một RunLoop trên luồng đó (nếu không thì phương thức ủy nhiệm của NSURLConnection sẽ không bao giờ được gọi).

+1

Mặc dù phần còn lại về cơ bản giống như câu trả lời của tôi, tôi không chắc mình đồng ý với câu cuối cùng; đây là thứ bạn có thể thực hiện cho 5 hoặc 4 trong khoảng 50 dòng mã để kết hợp thư viện của bên thứ ba lớn không giống như làm cho áp phích quảng cáo tốt hơn, đặc biệt là với các nhánh hỗ trợ và thử nghiệm. – Tommy

+0

@Tommy Đối với nhiệm vụ cụ thể này, tôi đồng ý rằng bạn không cần thư viện của bên thứ ba. Lý do tôi đề xuất sử dụng đó là tiến lên nếu bạn muốn thêm nhiều tính năng hơn vào lớp Utility của bạn như cách dễ dàng để xử lý các tham số REST, lưu trữ hình ảnh, v.v ... mặc dù bạn có thể tiếp tục thêm các tính năng dựa trên yêu cầu của mình, thiết kế của bạn có thể phức tạp và các khung bên thứ ba này có ích. – 0x8badf00d

0

Tôi sử dụng phương thức mặt tiền để mở hàng đợi một nhân viên nội bộ phát ra cuộc gọi đồng bộ. Tùy thuộc vào tốc độ bạn gửi cuộc gọi, nó có thể hoạt động. Ví dụ:

// Presented in @interface 
-(void)sendPostRequest { 
    // Last chance to update main thread/UI 
    NSInvocationOperation *op = [[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(sendPostRequest_internal) object:nil] autorelease]; 
    [opQueue addOperation:op]; 
} 

// Hidden in @implementation 
-(void)sendPostRequest_internal { 
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 

    NSURLRequest *request = // yadda, you might use NSURLMutableRequest 
    NSURLResponse *response = nil; 
    NSError *error = nil; 
    NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:response error:error]; 

    // process data, retain things as needed, post results using performSelectorOnMainThread: 

    [pool release]; 
} 

Nó hoạt động khá tốt cho mục đích của tôi nhưng bạn có thể cần nghiên cứu sâu hơn một chút về công cụ không đồng bộ, điều này thực sự không quá tệ.

1

Tôi có vấn đề này trước 5,0 vì vậy tôi làm một lớp học nhỏ để xử lý các giao thức đại biểu NSURLConnection và cung cấp người gọi một giao diện với đóng cửa:

BetterNSURLConnection.h

@property (retain, nonatomic) NSURLRequest *request; 

BetterNSURLConnection.m

@property (retain, nonatomic) NSURLConnection *connection; 
@property (retain, nonatomic) NSHTTPURLResponse *response; 
@property (retain, nonatomic) NSMutableData *responseData; 

@property (copy, nonatomic) void (^completionBlock)(id, NSHTTPURLResponse *); 
@property (copy, nonatomic) void (^errorBlock)(NSError *); 

... Bạn có thể thêm typedefs để làm cho những chữ ký khối đẹp hơn ... sau đó:

@synthesize connection = _connection; 
@synthesize response = _response; 
@synthesize responseData = _responseData; 
@synthesize completionBlock = _completionBlock; 
@synthesize errorBlock = _errorBlock; 
@synthesize request=_request; 


- (void)startWithCompletion:(void (^)(id, NSHTTPURLResponse *))completionBlock error:(void (^)(NSError *))errorBlock { 

    [UIApplication sharedApplication].networkActivityIndicatorVisible = YES; 

    self.completionBlock = completionBlock; 
    self.errorBlock = errorBlock; 
    self.responseData = [NSMutableData data]; 

    NSURLConnection *connection = [NSURLConnection connectionWithRequest:self.request delegate:self]; 
    self.connection = connection; 
    [self.connection start]; 
    [connection release]; 
} 

... sau đó làm các đại biểu như thế này:

- (void)connection:(NSURLConnection *)aConnection didReceiveResponse:(NSHTTPURLResponse *)response { 

    [self.responseData setLength:0]; 
    self.response = response; 
} 

- (void)connection:(NSURLConnection *)aConnection didReceiveData:(NSData *)data { 

    [self.responseData appendData:data]; 
} 

- (void)connection:(NSURLConnection *)aConnection didFailWithError:(NSError *)error { 

    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO; 
    self.errorBlock(error); 
    self.connection = nil; 
} 

- (void)connectionDidFinishLoading:(NSURLConnection *)aConnection { 

    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO; 

    if (self.response.statusCode >= 400) { 
     self.errorBlock(error); 
    } else { 
     // i do json requests, and call a json parser here, but you might want to do something different 
     id result = [self parseResponse:self.responseData]; 
     self.completionBlock(result, self.response); 
    } 
    self.connection = nil; 
} 
Các vấn đề liên quan