Malloc
Malloc

Reputation: 16276

Execute methods which call each other. All in background

In My application, I have few methods calling each other to parse XML downloaded feed, I need to make all parsing stuff in the background with NSOperation and NSOperationQueue as for now it's being executed in the main thread and freezing the whole app.

My App logic is something like the following:

-(IBAction) callSync{
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:APIURL]];

    AFURLConnectionOperation *operation = [[AFURLConnectionOperation alloc] initWithRequest:request];
    operation.completionBlock = ^{
          //1
          [self startParsing:operation.responseString];
    };
    [operation start];
}

//2
-(void)startParsing:(NSString*)str
{
         //some logic
         [self traverseXML:str];//Call traverseXML
}
//3
- (void) traverseXML:(TBXMLElement *)element {
        //Some logic
        [self saveFile:localWS];//CallsaveFile
}
//4
-(void) saveFile : (WorkFile *)_workFile{
        //Some logic
}

My question is: Should I subclass an NSOperation class for each method, I mean one for startParsing, and one for traverseXML, etc? or just it's enough to create one NSOperation subclass and do all implementation methods inside.

Upvotes: 1

Views: 180

Answers (4)

Rob
Rob

Reputation: 437452

As an aside, I'm not quite sure why your code is blocking the main thread. When I put a breakpoint in my completion block, for example, I can clearly see it's happening on a background thread (#5 in this example):

completion

Thus, I should be able to do whatever time consuming process I want there without affecting the UX (such as my command to sleep for five seconds, which in my sample project does not freeze the UI).

Just as a point of contrast, if I put a breakpoint in the progress block (which AFNetworking dispatches back to the main queue), it is on the main thread, as expected:

progress

Since that is happening on the main queue, I would have to be very careful about making sure I don't do anything too time consuming there, because that would block the UI.

(BTW, you might have to control-click on the above images to open them in another tab/window in your browser to see them legibly.)

But, bottom line, it strikes me that the main parsing process, if you're doing that in the completion block, should already run asynchronously. Sure, I might refine it to submit the AFURLConnectionOperation to some network queue and add the parse operation to a separate parse operation queue, but it strikes me that this code should already run asynchronously.

I only mention that because it strikes me that you're about to embark on a process of converting this parsing to a background operation, but it seems worthwhile confirming why your code is freezing your app in the first place.

For example, if the parsing process is using some locking mechanism, such as @synchronized, to synchronize the parsing (and when you do stuff asynchronously, you have to think long and hard about how you're going to synchronize it properly to ensure thread safety), simply moving this code to another background operation will not remedy that situation. Likewise, if your parser is dispatching some code to dispatch_get_main_queue(), you would have to refactor that, as well.

Upvotes: 0

Rob
Rob

Reputation: 437452

A couple of thoughts:

  1. You don't need to do any NSOperation subclassing. You can if you really want to, but seems entirely unnecessary in this situation. Much easier is if, for example, you had a separate operation queue for the parsing process, you can just do addOperationWithBlock or create a NSBlockOperation and add that to your parsing queue. For example:

    [self.parseQueue addOperationWithBlock:^{
        [self startParsing:operation.responseString]; // this will call traverseXML and saveFile, so if those are all synchronous, then all three are within this one block
    }];
    

    Personally, the only time I'm going through the extra work of subclassing NSOperation is when I have to implement my own custom cancellation logic or I'm creating an operation that encompasses some task that, itself, is asynchronous, and I want to control when the operation sets isFinished. Or I subclass NSOperation when the operation, itself, hits some significant level of complexity that abstracting it to a separate operation class improves code legibility. But nothing you've described so far suggests that you need to do NSOperation subclassing. And just using NSBlockOperation or, better, just addOperationWithBlock, is far simpler that subclassing NSOperation.

  2. So, setting aside the "subclassing NSOperation" question, let's turn to whether you want separate operations for your three methods or not. Given that you're performing these three tasks sequentially, then that initially sounds like a candidate for a single operation. You certainly could create three separate operations, if you wanted, but I don't see any compelling business case for that additional level of complexity.

  3. In my mind, the more interesting question is "what operation queues will I create". For that, it's a question of whether you want concurrency (very useful in network operations, for example) and to what degree (e.g. it's good to not issue too many concurrent network requests, namely, sticking max of four or five). And that's only an issue if you're downloading multiple XML files and parsing them. In that scenario, I could imagine that you might have one queue for the network operations, and another for the parsing operations. That way, you could configure your network queue to enjoy some concurrency, but constrain the maxConcurrentOperationCount so you don't have too many concurrent network requests. The parsing/saving operations may have different concurrency capabilities (e.g. if you're not instantiating a separate parser object, your parsing might not support concurrency at all). Often it comes down to balancing the performance gains of concurrency with the memory consumption and program complexity required of such concurrency.

Upvotes: 1

adali
adali

Reputation: 5977

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

    //parse
    //traverse
    //save

    dispatch_async (dispatch_get_main_queue (), ^{

        //update UI
    });
});

Upvotes: 0

Marcelo
Marcelo

Reputation: 9944

I'd go with one NSOperation, as the 3 operations you want to do don't actually live by themselves.

Perhaps only saving should be in another class, but I don't think it's a problem to do it on the same operation.

Upvotes: 0

Related Questions