在我的项目中,我需要将数据发送到服务器,因为我已经使用以下代码来完成任务:
- (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
通过上面的代码,我能够发送数据并获得响应。
但是当没有互联网时,数据将不会被发送到服务器。如何根据我们得到的响应来处理这种情况。
我们假设我们在最糟糕的情况下得到关于公平条件和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
答案 0 :(得分:5)
抬起头来!这是一个很长的答案。 TL; DR:您无法重新运行NSOperation
,但您可以设计类和方法,以便轻松重试请求。< / em>的
首先快速回答您的标题问题:您无法重新运行NSOperation
,但他们并不是为此而设计的。来自docs:
操作对象是单击对象 - 也就是说,它执行它 任务一次,不能再用来执行它。
有了这个,让我们来看看你目前正在做什么,并清理一下,以便重新使用它更容易。那里有大量不同寻常的东西,你不需要;我会一块一块地完成它。
让我们从您的operationQueueTask:
方法开始。你在方法中做的第一件事是:
dispatch_queue_t myQueue = dispatch_queue_create("SERVER_QUEUE",NULL);
这意味着每次调用该方法时,您都会创建一个新的调度队列。虽然你可以这样做,但如果你真的想要这样做,那不是真正为其设计的调度队列。更好的想法是使用一个已经可用的后台队列:
dispatch_queue_t myQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
接下来,您将异步调度块到该队列。那块:
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");
});
}];
现在,请注意您在NSURLConnection
上调用的方法的名称? send
的 Asynchronous
强> Request:
。它实际上为您处理在后台队列上排队请求。这意味着,您实际上并不需要(或想要)此方法开头的所有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
现在,转到您的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;
}
摆脱了你的第一线。接下来,您需要创建一个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");
});
}];
}
注意:我们仍然需要保留operationQueue
,因为我们将其作为应运行的队列传递给[NSURLConnection sendAsynchronousRequest:...
。 < / p>
那么,我们如何在失败时重试请求呢?最简单的方法是添加一个递归函数,该函数在请求失败时调用自身。您将此方法传递给您要发送的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];
}
现在,让我们构建递归函数。我打电话给它:
- (void)sendJSONToServer:(NSString*)jsonString withRetryAttempts:(NSUInteger)retryTimes;
基本流程将是:
retryTimes
是否大于0 retryTimes
中减去一个并再次调用此函数看起来像是:
- (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
}
}
注意:其中有一些位只是注释,因为它们是特定于应用程序的;他们需要在代码编译/运行之前实现。
现在,请拨打[yourClass sendJSONToServer:jsonString]
,而不是致电[yourClass sendJSONToServer:jsonString withRetryTimes:maxRetries]
,如果请求失败,则应重试maxRetries
次。
最后一点:正如@Deftsoft所提到的,Apple的Reachability类是一种很好的方式来了解你是否有与网络的有效连接。在尝试致电sendJSONToServer:withRetryTimes:
之前先检查一下是个好主意。这样,当您无法首先连接时,您就不会尝试发出请求。
答案 1 :(得分:0)
下面的Apple可访问性类是参考代码,它将为您提供更好的主意。
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkChanged:) name:kReachabilityChangedNotification object:nil];
reachability = [Reachability reachabilityForInternetConnection];
[reachability startNotifier];
- (void)networkChanged:(NSNotification *)notification
{
NetworkStatus remoteHostStatus = [reachability currentReachabilityStatus];
if(remoteHostStatus == NotReachable) { NSLog(@"not reachable");}
else if (remoteHostStatus == ReachableViaWiFiNetwork) { NSLog(@"wifi"); }
else if (remoteHostStatus == ReachableViaCarrierDataNetwork) { NSLog(@"carrier"); }
}