NSOperation Subclass for Internet Repeat Operations

I am subclassing NSOperation for http post in the background thread. These specific HTTP messages do not require returning any value.

What I'm trying to do is when I have an error or timeout, which I want it to send after increasing the delay (fibonacci).

So far I have done this:

NSInternetOperation.h:

#import <Foundation/Foundation.h>

@interface NSInternetOperation : NSOperation
@property (nonatomic) BOOL executing;
@property (nonatomic) BOOL finished;
@property (nonatomic) BOOL completed;
@property (nonatomic) BOOL cancelled;
- (id)initWebServiceName:(NSString*)webServiceName andPerameters:(NSString*)parameters;
- (void)start;
@end

NSInternetOperation.m:

#import "NSInternetOperation.h"

static NSString * const kFinishedKey = @"isFinished";
static NSString * const kExecutingKey = @"isExecuting";

@interface NSInternetOperation ()
@property (strong, nonatomic) NSString *serviceName;
@property (strong, nonatomic) NSString *params;
- (void)completeOperation;
@end

@implementation NSInternetOperation

- (id)initWebServiceName:(NSString*)webServiceName andPerameters:(NSString*)parameters
{
    self = [super init];
    if (self) {
        _serviceName = webServiceName;
        _params = parameters;
        _executing = NO;
        _finished = NO;
        _completed = NO;
    }
    return self;
}

- (BOOL)isExecuting { return self.executing; }
- (BOOL)isFinished { return self.finished; }
- (BOOL)isCompleted { return self.completed; }
- (BOOL)isCancelled { return self.cancelled; }
- (BOOL)isConcurrent { return YES; }

- (void)start
{
    if ([self isCancelled]) {
        [self willChangeValueForKey:kFinishedKey];
        self.finished = YES;
        [self didChangeValueForKey:kFinishedKey];
        return;
    }

    // If the operation is not cancelled, begin executing the task
    [self willChangeValueForKey:kExecutingKey];
    self.executing = YES;
    [self didChangeValueForKey:kExecutingKey];

    [self main];
}

- (void)main
{
    @try {
        //
        // Here we add our asynchronized code
        //
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            NSURL *completeURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@/%@", kWEB_SERVICE_URL, self.serviceName]];
            NSData *body = [self.params dataUsingEncoding:NSUTF8StringEncoding];
            NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:completeURL];
            [request setHTTPMethod:@"POST"];
            [request setValue:kAPP_PASSWORD_VALUE forHTTPHeaderField:kAPP_PASSWORD_HEADER];
            [request setHTTPBody:body];
            [request setValue:[NSString stringWithFormat:@"%lu", (unsigned long)body.length] forHTTPHeaderField:@"Content-Length"];
            [request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];


            if (__iOS_7_AND_HIGHER)
            {
                NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
                NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:[Netroads sharedInstance] delegateQueue:[NSOperationQueue new]];
                NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
                    if (error)
                    {
                        NSLog(@"%@ Error: %@", self.serviceName, error.localizedDescription);
                    }
                    else
                    {
                        //NSString *responseXML = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
                        //NSLog(@"\n\nResponseXML(%@):\n%@", webServiceName, responseXML);
                    }
                }];
                [dataTask resume];
            }
            else
            {
                [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue new] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
                    if (connectionError)
                    {
                        NSLog(@"%@ Error: %@", self.serviceName, connectionError.localizedDescription);
                    }
                    else
                    {
                        //NSString *responseXML = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
                        //NSLog(@"\n\nResponseXML(%@):\n%@", webServiceName, responseXML);
                    }
                }];
            }
        });

        [self completeOperation];
    }
    @catch (NSException *exception) {
        NSLog(@"%s exception.reason: %@", __PRETTY_FUNCTION__, exception.reason);
        [self completeOperation];
    }
}

- (void)completeOperation
{
    [self willChangeValueForKey:kFinishedKey];
    [self willChangeValueForKey:kExecutingKey];

    self.executing = NO;
    self.finished = YES;

    [self didChangeValueForKey:kExecutingKey];
    [self didChangeValueForKey:kFinishedKey];
}

@end
+4
source share
2 answers

A few reactions:

  • , , , [self completeOperation] NSURLSessionDataTask sendAsynchronousRequest. (, , , maxConcurrentOperationCount).

  • . , - :

    - (void)main
    {
        NSURLRequest *request = [self createRequest]; // maybe move the request creation stuff into its own method
    
        [self tryRequest:request currentDelay:1.0];
    }
    
    - (void)tryRequest:(NSURLRequest *)request currentDelay:(NSTimeInterval)delay
    {
        [NSURLConnection sendAsynchronousRequest:request queue:[self networkOperationCompletionQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
    
            BOOL success = NO;
    
            if (connectionError) {
                NSLog(@"%@ Error: %@", self.serviceName, connectionError.localizedDescription);
            } else {
                if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
                    NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode];
                    if (statusCode == 200) {
                        // parse XML response here; if successful, set `success` to `YES`
                    }
                }
            }
    
            if (success) {
                [self completeOperation];
            } else {
                dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC));
                dispatch_after(popTime, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
                    NSTimeInterval nextDelay = [self nextDelayFromCurrentDelay:delay];
                    [self tryRequest:request currentDelay:nextDelay];
                });
            }
        }];
    }
    
  • . , . , , Reachability , .

    ( Reachability), , .

:

  • , dispatch_async main , ( , , ).

  • try/catch, , /, . Cocoa NSError. Cocoa , , . . Apple Objective-C.

  • isExecuting isFinished getter, getter :

    @property (nonatomic, readwrite, getter=isExecuting) BOOL executing;
    @property (nonatomic, readwrite, getter=isFinished)  BOOL finished;
    
  • , setExecuting setFinished, , , , :

    @synthesize finished  = _finished;
    @synthesize executing = _executing;
    
    - (void)setExecuting:(BOOL)executing
    {
        [self willChangeValueForKey:kExecutingKey];
        _executing = executing;
        [self didChangeValueForKey:kExecutingKey];
    }
    
    - (void)setFinished:(BOOL)finished
    {
        [self willChangeValueForKey:kFinishedKey];
        _finished = finished;
        [self didChangeValueForKey:kFinishedKey];
    }
    

    , setter, , willChangeValueForKey didChangeValueForKey, .

  • , , isCancelled ( ). cancel, super, . , cancel, delegate, , [self isCancelled] didReceiveData.

    isCompleted isFinished. , completed isCompleted.

  • , , NSURLSession, NSURLConnection. , , , NSURLConnection - , ( NSURLSession iOS 7+, ). , , NSURLConnection, iOS, NSURLSession, , , - .

+6

:

static NSString * const kFinishedKey = @"isFinished";
static NSString * const kExecutingKey = @"isExecuting";

- (void)completeOperation
{
    [self willChangeValueForKey:kFinishedKey];
    [self willChangeValueForKey:kExecutingKey];

    self.executing = NO;
    self.finished = YES;

    [self didChangeValueForKey:kExecutingKey];
    [self didChangeValueForKey:kFinishedKey];
}

"isFinished" "isExecuting". NSOperationQueue , "" "" - "isFinished" "isExecuting" - get (read) .

These are not the key paths you were looking for

NSOperation KVO , KVO, +automaticallyNotifiesObserversForKey +automaticallyNotifiesObserversOf<Key>, NO. .

:

@property (nonatomic) BOOL executing;
@property (nonatomic) BOOL finished;
@property (nonatomic) BOOL cancelled;

NSOperation, get accessor. :

@property (nonatomic, getter=isExecuting) BOOL executing;
@property (nonatomic, getter=isFinished) BOOL finished;
@property (nonatomic, getter=isCancelled) BOOL cancelled;

NSOperation. NSOperation readonly , readwrite .

, Apple, , MVCNetworking

0

Source: https://habr.com/ru/post/1529646/


All Articles