Che*_*tty 2 iphone nsoperation nsoperationqueue ios
在我的项目中,我需要将数据发送到服务器,为此我使用以下代码来完成任务:
- (void)sendJSONToServer:(NSString *) jsonString
{
// Create a new NSOperationQueue instance.
operationQueue = [NSOperationQueue new];
//
// Create a new NSOperation object using the NSInvocationOperation subclass to run the operationQueueTask method
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self
selector:@selector(operationQueueTask:)
object:jsonString];
// Add the operation to the queue and let it to be executed.
[operationQueue addOperation:operation];
}//End of sendJSONToServer method
-(void) operationQueueTask:(NSString *) jsonString
{
//NSOperationQueue *remoteResultQueue = [[NSOperationQueue alloc] init];
dispatch_queue_t myQueue = dispatch_queue_create("SERVER_QUEUE",NULL);
dispatch_async(myQueue, ^{
// Performing long running process
// Sending json data to server asynchronously
NSData *postData = [jsonString dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:@"%lu", (unsigned long)[jsonString length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"MY_URL_eg_http://www.example.com"]];
[request setHTTPMethod:@"POST"];
[request setValue:postLength forHTTPHeaderField:@"Content-Length"];
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
[request setHTTPBody:postData];
[NSURLConnection sendAsynchronousRequest:request queue:operationQueue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
NSLog(@"Response is:%@",[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]);
}];
dispatch_async(dispatch_get_main_queue(), ^{
// Update the UI
NSLog(@"Thread Process Finished");
});
});
}//End of operationQueueTask method
Run Code Online (Sandbox Code Playgroud)
通过上面的代码,我能够发送数据并获得响应.
但是当没有互联网时,数据将不会被发送到服务器.如何根据我们得到的响应来处理这种情况.
假设我们在最恶劣的条件下得到了success有关公平状况的回应false.
更新后的代码
-(id)init
{
self = [super init];
if (self != nil)
{
//initialize stuffs here
pendingOperationQueue = [[NSMutableArray alloc] init];
operationQueue = [NSOperationQueue new];
}
return self;
}//End of init method
- (void)sendJSONToServer:(NSString *) jsonString
{
NSOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(operationQueueTask:) object:[NSString stringWithString:[pendingOperationQueue objectAtIndex:0]]];
[operation start];
}//End of sendJSONToServer method
-(void) operationQueueTask:(NSString *) jsonString
{
//NSOperationQueue *remoteResultQueue = [[NSOperationQueue alloc] init];
dispatch_queue_t myQueue = dispatch_queue_create("SERVER_QUEUE",NULL);
dispatch_async(myQueue, ^{
// Performing long running process
// Sending json data to server asynchronously
NSData *postData = [jsonString dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:@"%lu", (unsigned long)[jsonString length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"MY_URL_http://www/example.com"]];
[request setHTTPMethod:@"POST"];
[request setValue:postLength forHTTPHeaderField:@"Content-Length"];
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
[request setHTTPBody:postData];
[NSURLConnection sendAsynchronousRequest:request queue:operationQueue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
NSLog(@"Response is:%@",[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]);
if([[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding] rangeOfString:@"true"].location == NSNotFound)
{
// Add the operation to the queue and let it to be executed.
NSLog(@"Failed To Add To Server, Rerunning the task");
}
else
{
NSLog(@"Successfully Added To Server");
NSLog(@"ADDED_DATA_TO_SERVER: %@", jsonString);
if([pendingOperationQueue count] > 0)
{
[pendingOperationQueue removeObjectAtIndex:0];
if([pendingOperationQueue count] > 0)
{
NSOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(operationQueueTask:) object:[NSString stringWithString:[pendingOperationQueue objectAtIndex:0]]];
[operation start];
}
}
}
}];
});
}//End of operationQueueTask method
Run Code Online (Sandbox Code Playgroud)
当心!这是一个很长的答案. TL; DR:您无法重新运行NSOperation,但您可以设计类和方法,以便轻松重试请求.
首先快速回答你的标题问题:你不能重新运行NSOperation,他们不是为了那样做.来自文档:
操作对象是单击对象 - 也就是说,它执行一次任务,不能再用于执行它.
有了这个,让我们来看看你目前正在做什么,并清理一下,以便重新使用它更容易.那里有很多你不需要的异步东西; 我会一点一点地完成它.
让我们从你的operationQueueTask:方法开始吧.你在方法中做的第一件事是:
dispatch_queue_t myQueue = dispatch_queue_create("SERVER_QUEUE",NULL);
Run Code Online (Sandbox Code Playgroud)
这意味着每次调用该方法时,您都在创建一个新的调度队列.虽然你可以这样做,但如果你真的想要,那不是调度队列的真正设计目的.更好的想法是使用已经可用的后台队列之一:
dispatch_queue_t myQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
Run Code Online (Sandbox Code Playgroud)
接下来,您将异步调度块到该队列.那块:
NSMutableURLRequest.[NSURLConnection sendAsynchronousRequest:...].1和2都很好,我没有看到你需要改变的东西.但是,由于调用该调度的位置,因此存在问题.现在设置它的方式,NSURLConnection将触发其异步请求,然后,在有机会运行之前,您将块关闭到主队列以更新UI.你需要做的是在传递给它的完成处理程序中触发该块[NSURLConnection sendAsynchronousRequest:...].像这样:
[NSURLConnection sendAsynchronousRequest:request queue:operationQueue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
NSLog(@"Response is:%@",[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]);
dispatch_async(dispatch_get_main_queue(), ^{
// Update the UI
NSLog(@"Thread Process Finished");
});
}];
Run Code Online (Sandbox Code Playgroud)
现在,请注意您正在呼叫的方法的名称NSURLConnection?.它实际上为您处理在后台队列上排队请求.这意味着,您实际上并不需要(或想要)此方法开头的所有内容.考虑到这一点,我们可以将其减少到:sendAsynchronousRequest:dispatch_*
-(void) operationQueueTask:(NSString *) jsonString
{
NSData *postData = [jsonString dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:@"%lu", (unsigned long)[jsonString length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"MY_URL_eg_http://www.example.com"]];
[request setHTTPMethod:@"POST"];
[request setValue:postLength forHTTPHeaderField:@"Content-Length"];
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
[request setHTTPBody:postData];
[NSURLConnection sendAsynchronousRequest:request queue:operationQueue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
NSLog(@"Response is:%@",[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]);
dispatch_async(dispatch_get_main_queue(), ^{
// Update the UI
NSLog(@"Thread Process Finished");
});
}];
} //End of operationQueueTask method
Run Code Online (Sandbox Code Playgroud)
现在,关于你的sendJSONToServer:方法.你在这里开始做类似的事情operationQueueTask::你NSOperationQueue每次运行时都会创建一个新的; 这也是不需要的(通常也不需要).你应该做的就是operationQueue在你的类被初始化时创建它(看起来它已经是你班上的一个实例变量,所以你在那里很好):
// NOTE: I'm just using a default initializer here; if you already have an initializer, use that instead
- (instancetype)init {
if (self = [super init]) {
operationQueue = [NSOperationQueue new];
}
return self;
}
Run Code Online (Sandbox Code Playgroud)
这摆脱了你的第一线.接下来,您正在创建一个NSInvocationOperation调用operationQueueTask:,然后将其添加到您的operationQueue.由于您operationQueue每次都在重新创建,我将假设它不会用于除这些服务器请求之外的任何其他内容.在这种情况下,您实际上根本不需要这样做operationQueue,因为正如我们在前面的方法中发现的那样,NSURLConnection已经为您处理了所有后台线程.在这种情况下,我们实际上只需将代码复制operationQueueTask:到sendJSONToServer:并operationQueueTask:完全摆脱.这使它看起来像:
- (void)sendJSONToServer:(NSString*)jsonString {
NSData *postData = [jsonString dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:@"%lu", (unsigned long)[jsonString length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"MY_URL_eg_http://www.example.com"]];
[request setHTTPMethod:@"POST"];
[request setValue:postLength forHTTPHeaderField:@"Content-Length"];
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
[request setHTTPBody:postData];
[NSURLConnection sendAsynchronousRequest:request queue:operationQueue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
NSLog(@"Response is:%@",[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]);
dispatch_async(dispatch_get_main_queue(), ^{
// Update the UI
NSLog(@"Thread Process Finished");
});
}];
}
Run Code Online (Sandbox Code Playgroud)
注意:我们仍然需要保留operationQueue,因为我们将它传递给[NSURLConnection sendAsynchronousRequest:...它应该运行的队列.
那么,我们如何在失败时重试请求呢?最简单的方法是添加一个递归函数,该函数在请求失败时调用自身.您将传递此jsonString要发送的方法以及在放弃之前应尝试发送的最大次数.
为方便起见,让我们对您现有的函数进行一次更改:不是处理函数内部的完成块,而是让完成块成为您传递给函数的参数,以便可以在其他地方处理.
- (void)sendJSONToServer:(NSString*)jsonString withCompletionHandler:(void (^)(NSURLResponse *response, NSData *data, NSError *connectionError))completionHandler {
NSData *postData = [jsonString dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:@"%lu", (unsigned long)[jsonString length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"MY_URL_eg_http://www.example.com"]];
[request setHTTPMethod:@"POST"];
[request setValue:postLength forHTTPHeaderField:@"Content-Length"];
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
[request setHTTPBody:postData];
[NSURLConnection sendAsynchronousRequest:request queue:operationQueue completionHandler:completionHandler];
}
Run Code Online (Sandbox Code Playgroud)
现在,让我们构建递归函数.我会称之为:
- (void)sendJSONToServer:(NSString*)jsonString withRetryAttempts:(NSUInteger)retryTimes;
Run Code Online (Sandbox Code Playgroud)
基本流程将是:
retryTimes大于0retryTimes并再次调用此函数看起来像是这样的:
- (void)sendJSONToServer:(NSString*)jsonString withRetryAttempts:(NSUInteger)retryTimes {
if (retryTimes > 0) {
[self sendJSONToServer:jsonString withCompletionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
NSLog(@"Response is:%@",[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]);
if (/* check response to make sure it succeeded */) {
dispatch_async(dispatch_get_main_queue(), ^{
// Update the UI
NSLog(@"Thread Process Finished");
});
} else {
// Note: you can add a dispatch_after here (or something similar) to wait before the next attempt
// You could also add exponential backoff here, which is usually good when retrying network stuff
[self sendJSONToServer:jsonString withRetryAttempts:(retryTimes - 1)];
}
}];
} else {
// We're out of retries; handle appropriately
}
}
Run Code Online (Sandbox Code Playgroud)
注意:其中有一些位只是注释,因为它们是特定于应用程序的; 它们需要在代码编译/运行之前实现.
现在,不是调用,而是[yourClass sendJSONToServer:jsonString]调用:[yourClass sendJSONToServer:jsonString withRetryTimes:maxRetries]并且,如果请求失败,它应该重试maxRetries多次.
最后一点:正如@Deftsoft所提到的,Apple的Reachability类是一种很好的方式来了解你是否有与网络的有效连接.在尝试打电话之前先检查一下是个好主意sendJSONToServer:withRetryTimes:.这样,当你无法连接时,你就不会尝试发出请求.
| 归档时间: |
|
| 查看次数: |
1696 次 |
| 最近记录: |