Reputation: 10818
I just started using AFNetworking and I'm trying to learn how to do it correctly.
I subclass the AFHTTPClient
and created my own MyAppClient
with the right base URL.
I'm communicating with my server with HTTP POST request and server response with xml.
for sending a request I do:
[[MyAppClient sharedClient] postPath:somePath parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
// need to parse the data here...
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
//
NSLog(@"%@", [error localizedDescription]);
}];
Few questions:
Why does the AFHTTPClient
use operation if anyway it's using an asynchronous NSURLConnection
that doesn't block the main thread?
After I'm getting the data I need to parse it, should I now create a new operation to parse the data?
It seems to me that it would be better to parse the data also in the operation and then return the parsed objects no?
At the same subject I have a custom generic XMLParser class that gets a NSData
and parse it to NSDictionary
, and I would like to use it for all of the responses, how can I integrate it in the AFHTTPClient
AFHTTPRequestOperation
so that the response will be already parsed?
Upvotes: 4
Views: 665
Reputation: 7663
1) In AFNetworking - Operation is the most granular level. You can either start it or enque them. AFHTTPClient is great if you are enqueuing requests. Look at enqueueOperation
2) AFNetworking keeps it modular. Essentially you are not tied to their parser. You can use any xml parser to parse data. Though I like (DCKeyValue)[https://github.com/dchohfi/KeyValueObjectMapping] to parse your data and directly converting them to objects.
3)You cannot automatically parse like RestKit but answer 2 should help you.
Upvotes: 0
Reputation: 19116
Why does the AFHTTPClient use operation if anyway it's using an asynchronous NSURLConnection that doesn't block the main thread?
This is actually an excellent question.
In fact, a viable "HTTPRequestOperation" class is not all required to subclass from NSOperation
. The design of AFHTTPRequestOperation
is very likely based on the "original" design introduced by an Apple engineer, "Quinn", who invented the first "reference design" with his class QHTTPOperation
and provided also a number of invaluable samples - which are still highly recommended and worth to take a look. This first design subclasses a NSOperation
and encapsulates a NSURLConnection
object.
This design has a number of advantages:
Since it's a subclasses of NSOperation
, the network request then looks like an "asynchronous operation". That means basically, the network request then has the principal methods start
and cancel
and has a completion handler to signal the eventual result of the request. This generic API is important for an asynchronous network operation so that it becomes a more general asynchronous operation.
Since its a class, it encapsulates all related state variables for a request. For example, the request, the response, the response data, an error (if any), and a couple more relevant state variables. The "network request object" then becomes quite handy to use, unlike the delegate approach where it starts to become difficult when more than one request should be handled in the delegate methods in one delegate object.
An NSOperation
object can be queued into a NSOperationQueue
. This makes it possible to define the order of the requests, and specifically any other operation, and the number of simultaneous active operations (requests) if you have many.
With NSOperation
one can define more or less complex dependencies among other operations, which lets you add some additional layer of "business logic". Occasionally, this becomes quite handy to solve more complex asynchronous problems.
So, the question why an already asynchronous NSURLConnection
was encapsulated in a subclass of NSOperation
are these aforementioned advantages. The reason was never to wrap it like a synchronous function into an NSOperation
so that it can be executed in a NSOperationQueue
.
In fact, there is a broad misconception about this. It appears, many people think the methods of the network request operation will be executed on the execution context of the NSOperation
(for example when added to a NSOperationQueue). However, this is not the case (with possible small exceptions among the various other implementations). The execution context of the methods (mostly delegate methods of NSULRConnection
) is a dedicated private thread which will be created by the NSOperation
subclass. The lower level functions from the underlaying NSURLConnection
also execute on their private execution context (one or more threads) anyway.
Only the start
method will be executed on the operation's execution context, which returns quickly. That is, if there is a queue (say dispatch queue or an NSOperationQueue) where the operation has been dispatched, only the start
method executes on the queue's execution context.
The NSOperation
's isFinished
state however, will be deferred up to the point where the network request indeed finished. This state has important meaning for other NSOperation
objects and an NSOperationQueue
: it signals the queue and other operations that this request has been finished.
So, the NSOperation
is less a vehicle to define the execution context of the functions of a network request, but rather a means to organize and setup relations to other operations.
After I'm getting the data I need to parse it, should I now create a new operation to parse the data? It seems to me that it would be better to parse the data also in the operation and then return the parsed objects no?
Well, you can do that. However, I wouldn't consider this a good design decision: an operation should only process a particular task. The network operation is the one, and the parse task _is another task, which can be an operation, too.
One reason for this is that operations can be "classified" regarding which system resources they mainly require: CPU, memory, IO, etc. Merging different "task types" makes it impossible to take advantage to associate them to dedicated queues in order to control utilization of system resources (see below).
Well, you can make a parse task an operation, of course. Whether this makes sense, depends, though:
The decision whether you want to make a particular task (or function) an NSOperation
depends on the following considerations:
An "Operation" is justified, if that task may take a long time to finish (from a user's perspective) and thus you (as a developer) want the user give a chance to cancel the task: (you remember: an asynchronous operation has the principal method cancel
)
Another reason would be to associate an operation to a particular execution context which itself is associated to a particular shared and limited system resource - like CPU, memory, IO etc. This lets you control for example the number of parallel executed operations which require a certain system resource. Say, you have a "Disk bound" task. In this case, you may create a NSOperationQueue
whose number of concurrent operation is 1, and giving it a particular "role" and a suitable name, for example "DiskBoundQueue". The queue helps you control the creation and the start of operations, and forces a limit of the number of parallel executing operations so that the restricted system resource won't be exhausted. Then, one would add "disk-bound" operations only to the dedicated "DiskBoundQueue". Since a disk operates suboptimal when accessed from different tasks simultaneously, the number of concurrent operations is set to 1. That is, such dedicated queues help optimize utilization of system resources.
If you have dependencies between operations, say you want to start operation C only if operation A AND operation B has been finished successfully. NSOperation
provide a means to establish such dependencies.
Yet another reason might be to control concurrent access to shared resources: if there are several operations which access a certain shared resource (an ivar for example) which are added to a serial NSOperationQueue
, the access to the shared resource is serialized and thus "thread safe". However if concurrency is the only requirement, I would prefer to use the simpler approach utilizing dispatch queues and blocks.
So, and in order to be more precise regarding your question: no, an NSOperation
would be likely oversized. You may be better of to use a dedicated dispatch queue, possibly a serial one which also solves concurrent access of shared resources.
At the same subject I have a custom generic XMLParser class that gets a NSData and parse it to NSDictionary, and I would like to use it for all of the responses, how can I integrate it in the AFHTTPClient AFHTTPRequestOperation so that the response will be already parsed?
A viable approach would just start the XML parser within the completion handler of a AFHTTPRequestOperation
or the AFClient for example.
If you have another operation which is dependent on the XML parser's result, then one approach is to encapsulate the XML parser in an NSOperation
, and then make the other operation dependent on the XML parser operation. (There are other and simpler solutions for such dependencies, though)
Upvotes: 2
Reputation: 972
This doesn't immediately make sense. My guess is that using standard mechanisms provides the best performance. However, the success/failure-blocks are also executed in the operation.
I do all the parsing in the success-blocks of the operations. If you use JSON, you can also configure AFNetworking to do the de-/serialization automatically for you. You might want to convert those dictionaries and arrays into smarter classes, though.
Have a look at AFJSONRequestOperation. It might give you some hints.
Upvotes: 0