The original link
In “iOS Networking — NSURLSession”, we introduced the architecture of NSURLSession and how it works. In this article, we introduce the design architecture of AFNetworking by reading the source code (version 2.6.3).
AFNetwoking overview
AFNetworking is a network library for iOS, macOS, watchOS and tvOS. Versions after AFNetworking 2.0 are built on top of the Foundation URL loading system based on NSURLSession. AFNetworking extends the powerful high-level network abstractions built into Cocoa, with a modular design and rich features, and is one of the most widely used open source projects.
AFNetworking architecture
The AFNetworking framework consists of two parts:
- AFNetworking core features
- UIKit+AFNetworking classification function
Here we mainly introduce the core functions of AFNetworking, and its overall architecture is shown in the figure below. AFNetworking consists of six classes:
- AFURLSessionManager: the core class of AFNetworking.
AFHTTPSessionManager
:AFURLSessionManager
A subclass of, primarily for HTTP requests.- AFURLRequestSerialization: request the serializer, used for the parameters of the code for the query string, the HTTP body, and according to the need to set the appropriate HTTP header fields.
- AFURLResponseSerialization: response serializer, used in the data decoding as an object, can also respond to incoming and data validation.
- AFSecurityPolicy: Evaluates the server’s trust in fixed X.509 certificates and public keys over a secure connection.
- AFNetworkReachabilityManager: monitor network accessibility.
Now let’s take a look at the core of AFNetworking — AFurlssession Manager.
AFURLSessionManager
The following figure shows the internal structure of AFURLSessionManager. Next, we will use the structure diagram as a guide to introduce the working principle of AFurlssession Manager.
Hold the property
Let’s start by looking at the properties held by the Afurlssession Manager.
/// External public attributes
@interface AFURLSessionManager
/// Manage Session
@property (readonly.nonatomic.strong) NSURLSession *session;
/// Queue for agent callback execution
@property (readonly.nonatomic.strong) NSOperationQueue *operationQueue;
/ / / using dataTaskWithRequest: success, failure: create, using the GET or POST data of execution task response. The default value is AFJSONResponseSerializer
@property (nonatomic.strong) id <AFURLResponseSerialization> responseSerializer;
/// Session Specifies the security policy used to evaluate the server's trust in secure connections. AFURLSessionManager uses the defaultPolicy defaultPolicy
@property (nonatomic.strong) AFSecurityPolicy *securityPolicy;
/// Network accessibility manager. AFURLSessionManager Uses sharedManager by default
@property (readwrite.nonatomic.strong) AFNetworkReachabilityManager *reachabilityManager;
/// ---------------------
/// Managed session All types of tasks currently running, such as data, upload, download, etc
@property (readonly.nonatomic.strong) NSArray <NSURLSessionTask *> *tasks;
/// Managed session Data task currently running
@property (readonly.nonatomic.strong) NSArray <NSURLSessionDataTask *> *dataTasks;
/// Managed session Upload task currently running
@property (readonly.nonatomic.strong) NSArray <NSURLSessionUploadTask *> *uploadTasks;
/// Managed session Current download task
@property (readonly.nonatomic.strong) NSArray <NSURLSessionDownloadTask *> *downloadTasks;
/// The queue of the completionBlock execution. If NULL, it is executed on the main thread
@property (nonatomic.strong.nullable) dispatch_queue_t completionQueue;
/// completionBlock The group of the execution. If NULL, it is executed in a private group
@property (nonatomic.strong.nullable) dispatch_group_t completionGroup;
@end
Copy the code
@interface AFURLSessionManager () @property (readWrite, nonatomic, strong) NSURLSessionConfiguration *sessionConfiguration; @property (readwrite, nonatomic, strong) NSOperationQueue *operationQueue; @property (readwrite, nonatomic, strong) NSMutableDictionary *mutableTaskDelegatesKeyedByTaskIdentifier; @property (readonly, nonatomic, copy) NSString *taskDescriptionForSessionTasks;
@property (readwrite, nonatomic, strong) NSLock *lock;
/// ---------------------
@property (readwrite, nonatomic, copy) AFURLSessionDidBecomeInvalidBlock sessionDidBecomeInvalid;
@property (readwrite, nonatomic, copy) AFURLSessionDidReceiveAuthenticationChallengeBlock sessionDidReceiveAuthenticationChallenge;
@property (readwrite, nonatomic, copy) AFURLSessionDidFinishEventsForBackgroundURLSessionBlock didFinishEventsForBackgroundURLSession AF_API_UNAVAILABLE(macos);
@property (readwrite, nonatomic, copy) AFURLSessionTaskWillPerformHTTPRedirectionBlock taskWillPerformHTTPRedirection;
@property (readwrite, nonatomic, copy) AFURLSessionTaskDidReceiveAuthenticationChallengeBlock taskDidReceiveAuthenticationChallenge;
@property (readwrite, nonatomic, copy) AFURLSessionTaskNeedNewBodyStreamBlock taskNeedNewBodyStream;
@property (readwrite, nonatomic, copy) AFURLSessionTaskDidSendBodyDataBlock taskDidSendBodyData;
@property (readwrite, nonatomic, copy) AFURLSessionTaskDidCompleteBlock taskDidComplete;
#if AF_CAN_INCLUDE_SESSION_TASK_METRICS
@property (readwrite, nonatomic, copy) AFURLSessionTaskDidFinishCollectingMetricsBlock taskDidFinishCollectingMetrics;
#endif
@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidReceiveResponseBlock dataTaskDidReceiveResponse;
@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidBecomeDownloadTaskBlock dataTaskDidBecomeDownloadTask;
@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidReceiveDataBlock dataTaskDidReceiveData;
@property (readwrite, nonatomic, copy) AFURLSessionDataTaskWillCacheResponseBlock dataTaskWillCacheResponse;
@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading;
@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidWriteDataBlock downloadTaskDidWriteData;
@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidResumeBlock downloadTaskDidResume;
@end
Copy the code
Initialize the
First of all, AFURLSessionManager object holds a NSURLSessionConfiguration object (session configuration object). A session configuration object is used to initialize and hold an NSURLSession object (session object). The initialization of the session object is as follows:
- (NSURLSession *)session {
@synchronized (self) {
if(! _session) { _session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue]; }}return _session;
}
Copy the code
Note that after initializing the session object, the session object sets AFURLSessionManager as its own agent. Therefore, the Afurlssession Manager needs to implement the methods declared by the relevant protocols. These agreements are:
NSURLSessionDelegate
NSURLSessionTaskDelegate
NSURLSessionDataDelegate
NSURLSessionDownloadDelegate
To enable developers to implement custom handling of session – and task-level events, AFURLSessionManager provides a number of methods for setting callbacks, covering almost every method declared by the protocol. These callbacks are held internally by AFURLSessionManager and are executed only when the corresponding protocol method is called.
Schedule management
AFURLSessionManager acts as a proxy for session objects and can basically process all task objects. However, it is cumbersome and unreasonable to use AFurlssession Manager to manage the progress of uploading and downloading tasks. The main reasons are as follows:
- A progress object needs to be held throughout the life cycle of both upload and download tasks
NSProgress
. - You also need to hold a receive data object for continuous receipt of data throughout the life of the download task.
AFURLSessionManager
Primarily used to manage sessions, it should not manage the details of task objects according to the single responsibility principle.
Therefore, AFNetworking provides AFURLSessionManagerTaskDelegate to a class to manage the progress of a specific task, data.
AFURLSessionManager internal mutableTaskDelegatesKeyedByTaskIdentifier maintains a mutable dictionary attributes, with task object identifier taskIdentifier as the key, Taking AFURLSessionManagerTaskDelegate object as the value. The initialization task object, is binding a AFURLSessionManagerTaskDelegate object, to handle the progress, download the data. The specific code is as follows:
// Create an NSURLSessionDataTask based on the specific request
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
uploadProgress:(nullable void(^) (NSProgress *uploadProgress)) uploadProgressBlock
downloadProgress:(nullable void(^) (NSProgress *downloadProgress)) downloadProgressBlock
completionHandler:(nullable void(^) (NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler
{
// Create a task object
__block NSURLSessionDataTask *dataTask = nil;
url_session_manager_create_task_safely(^{
dataTask = [self.session dataTaskWithRequest:request];
});
// Set the proxy object, upload progress callback, download progress callback, complete callback for the task object
[self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
return dataTask;
}
- (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask
uploadProgress:(nullable void(^) (NSProgress *uploadProgress)) uploadProgressBlock
downloadProgress:(nullable void(^) (NSProgress *downloadProgress)) downloadProgressBlock
completionHandler:(void(^) (NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
/ / create a AFURLSessionManagerTaskDelegate object for processing progress, download data
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:dataTask];
delegate.manager = self;
delegate.completionHandler = completionHandler;
// Identifies the SessionManager to which the task object belongs
dataTask.taskDescription = self.taskDescriptionForSessionTasks;
[self setDelegate:delegate forTask:dataTask];
delegate.uploadProgressBlock = uploadProgressBlock;
delegate.downloadProgressBlock = downloadProgressBlock;
}
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
forTask:(NSURLSessionTask *)task
{
NSParameterAssert(task);
NSParameterAssert(delegate);
/ / save NSURLSessionTask < - > AFURLSessionManagerTaskDelegate mapping relationship
[self.lock lock];
self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
[self addNotificationObserverForTask:task];
[self.lock unlock];
}
Copy the code
AFURLSessionManagerTaskDelegate mainly implements the following agreement method. In these methods to achieve progress management, download management. AFURLSessionManager although also implement these agreements, but will eventually call AFURLSessionManagerTaskDelegate in implementation.
NSURLSessionTaskDelegate
URLSession:task:didCompleteWithError:
URLSession:task:didFinishCollectingMetrics:
NSURLSessionDataDelegate
URLSession:dataTask:didReceiveData:
URLSession:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:
Inform the forwarding
NSURLSession defines a set of notifications. In order to provide a better encapsulation, AFURLSessionManager captures these notifications, converts them into its own defined notifications, and forwards them.
Here, the taskDescription property of the task object is used. The taskDescription attribute describes the Session Manager to which the task object belongs. Multiple AFurlsessionManagers may be initialized in your application. The Session Manager encapsulates and forwards notifications only after it captures them from the task objects it manages. Such as:
AFNetworkingTaskDidResumeNotification
AFNetworkingTaskDidSuspendNotification
AFHTTPSessionManager
Next, we will introduce AFURLSessionManager subclass AFHTTPSessionManager.
First, let’s look at the attributes that are unique to Afhttpssession Manager.
@interface AFHTTPSessionManager : AFURLSessionManager <NSSecureCoding.NSCopying>
@property (readonly.nonatomic.strong.nullable) NSURL *baseURL;
@property (nonatomic.strong) AFHTTPRequestSerializer <AFURLRequestSerialization> * requestSerializer;
@property (nonatomic.strong) AFHTTPResponseSerializer <AFURLResponseSerialization> * responseSerializer;
@property (nonatomic.strong) AFSecurityPolicy *securityPolicy;
@end
Copy the code
Since afhttpssession Manager is primarily used for HTTP requests, it provides a request serializer and a response serializer for HTTP requests, with default values set to HTTP and JSON, respectively.
In addition, AFHTTPSessionManager also encapsulates some of the complex task object initialization methods of AFURLSessionManager and provides some very simple convenience methods to serve various HTTP requests. As follows:
- (nullable NSURLSessionDataTask *)GET:(NSString *)URLString
parameters:(nullable id)parameters
headers:(nullable NSDictionary <NSString *, NSString *> *)headers
progress:(nullable void(^) (NSProgress *downloadProgress))downloadProgress
success:(nullable void(^) (NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void(^) (NSURLSessionDataTask * _Nullable task, NSError *error))failure;
- (nullable NSURLSessionDataTask *)HEAD:(NSString *)URLString
parameters:(nullable id)parameters
headers:(nullable NSDictionary <NSString *, NSString *> *)headers
success:(nullable void(^) (NSURLSessionDataTask *task))success
failure:(nullable void(^) (NSURLSessionDataTask * _Nullable task, NSError *error))failure;
- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
parameters:(nullable id)parameters
headers:(nullable NSDictionary <NSString *, NSString *> *)headers
constructingBodyWithBlock:(nullable void(^) (id <AFMultipartFormData> formData))block
progress:(nullable void(^) (NSProgress *uploadProgress))uploadProgress
success:(nullable void(^) (NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void(^) (NSURLSessionDataTask * _Nullable task, NSError *error))failure;
- (nullable NSURLSessionDataTask *)PUT:(NSString *)URLString
parameters:(nullable id)parameters
headers:(nullable NSDictionary <NSString *, NSString *> *)headers
success:(nullable void(^) (NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void(^) (NSURLSessionDataTask * _Nullable task, NSError *error))failure;
- (nullable NSURLSessionDataTask *)PATCH:(NSString *)URLString
parameters:(nullable id)parameters
headers:(nullable NSDictionary <NSString *, NSString *> *)headers
success:(nullable void(^) (NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void(^) (NSURLSessionDataTask * _Nullable task, NSError *error))failure;
- (nullable NSURLSessionDataTask *)DELETE:(NSString *)URLString
parameters:(nullable id)parameters
headers:(nullable NSDictionary <NSString *, NSString *> *)headers
success:(nullable void(^) (NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void(^) (NSURLSessionDataTask * _Nullable task, NSError *error))failure;
Copy the code
conclusion
In a word, AFNetworking is actually a high encapsulation of NSURLSession. It provides some easy-to-use apis for us to make network requests in iOS development and build network layer components faster and provide reasonable interfaces on it.
Later, we will take a closer look at AFNetworking’s other auxiliary classes and classification functions.
reference
- AFNetworking
- Overview of AFNetworking (1)
(after)