AFNetworking, as read in this article, is version 3.2.0.

Completed the first face AFURLRequestSerialization, AFURLResponseSerialization, AFSecurityPolicy and AFNetworkReachabilityManager class reading, Next, you can read AFNetworking’s core class, AFURLSessionManager. The core of the AFURLSessionManager class can be seen from the initial reference to the header file:

#import "AFURLResponseSerialization.h"
#import "AFURLRequestSerialization.h"
#import "AFSecurityPolicy.h"
#if ! TARGET_OS_WATCH
#import "AFNetworkReachabilityManager.h"
#endif
Copy the code

The previous reading of the above kind is to pave the way for this kind of reading.

1. Interface file

1.1. The attribute

/** Network session manager, property is read-only. Want to know NSURLSession use we can see this article: use NSURLSession (https://www.jianshu.com/p/fafc67475c73) * / @ property (readonly, nonatomic, strong) NSURLSession *session; /** Operation queue, also read-only, Max concurrency set to 1, used for proxy callbacks */ @property (readonly, nonatomic, strong) NSOperationQueue *operationQueue; /** The default value is' AFJSONResponseSerializer ', which handles json data. Cannot set to ` nil ` * / @ property (nonatomic, strong) id < AFURLResponseSerialization > responseSerializer; /** the securityPolicy object used to ensure a secure connection. The default is' defaultPolicy '*/ @property (nonatomic, strong) AFSecurityPolicy *securityPolicy; /** Network health monitoring manager, Defaults to ` sharedManager ` * / @ property (readwrite nonatomic, strong) AFNetworkReachabilityManager * reachabilityManager;Copy the code
  • Get the Session Tasks
/** All tasks created in the current 'session' are equivalent to the sum of the following three attributes: */ @property (readonly, nonatomic, strong) NSArray <NSURLSessionTask *> *tasks; /** * all 'dataTasks' created in current' session '*/ @property (readonly, nonatomic, strong) NSArray <NSURLSessionDataTask *> *dataTasks; /** All 'uploadTasks' created by the current' session '*/ @property (readonly, nonatomic, strong) NSArray <NSURLSessionUploadTask *> *uploadTasks; /** All 'downloadTasks' created by the current' session '*/ @property (readonly, nonatomic, strong) NSArray <NSURLSessionDownloadTask *> *downloadTasks;
Copy the code
  • Manage the callback queue
*/ @property (nonatomic, strong, nullable) dispatch_queue_t completionQueue; */ @property (nonatomic, strong, nullable) dispatch_group_t completionGroup;Copy the code
  • Resolve system errors
/** As of iOS 7.0, creating background upload tasks will sometimes return nil. If set to YES, AFNetworking follows Apple's advice that it will be recreated if the creation fails, with three attempts by default. The default is NO * / @ property (nonatomic, assign) BOOL attemptsToRecreateUploadTasksForBackgroundSessions;Copy the code

Method of 1.2.

  • Initialization method
/ * * by specifying NSURLSessionConfiguration object initialization object * / - (instancetype) initWithSessionConfiguration: [nullable NSURLSessionConfiguration *)configuration NS_DESIGNATED_INITIALIZER; /** When YES is passed, the current network session is closed immediately; When the NO, wait for after completion of the task to close the current dialog * / - (void) invalidateSessionCancelingTasks (BOOL) cancelPendingTasks;Copy the code
  • Create the NSURLSessionDataTask object method
/** create NSURLSessionDataTask */ - (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler DEPRECATED_ATTRIBUTE; /** Create NSURLSessionDataTask with the specified NSURLRequest object and return the corresponding upload and download progress, but the upload and download progress is not called in the main thread. Call */ - (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;Copy the code
  • Create the NSURLSessionUploadTask object method
/** Create NSURLSessionUploadTask with the specified NSURLRequest object and the URL of the local file to be uploaded, again, */ - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler; /** Create NSURLSessionUploadTask with the specified NSURLRequest object and the binary type data to be uploaded. */ - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromData:(nullable NSData *)bodyData progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler; /** Create NSURLSessionUploadTask with the NSURLRequest object of the specified data stream. Upload progress callback also is in the child thread * / - (NSURLSessionUploadTask *) uploadTaskWithStreamedRequest: (NSURLRequest *) request progress: (nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler;Copy the code
  • Create the NSURLSessionDownloadTask object method
/** Create the NSURLSessionDownloadTask with the specified NSURLRequest object. The callback to the upload progress is in the child thread. During the download process, the file will be placed in a temporary path targetPath. And automatically deletes the files in the original path. However, if the configuration parameter used to create the session is set to background mode, the information in destination will be lost when the application is killed. So the best way to in - setDownloadTaskDidFinishDownloadingBlock: set in the download file save the path of * / - (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgressBlock destination:(nullable NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination completionHandler:(nullable void (^)(NSURLResponse *response, NSURL * _Nullable filePath, NSError * _Nullable error))completionHandler; /** Create the NSURLSessionDownloadTask from the previously downloaded data, resume the download, Other and the above method is the same * / - (NSURLSessionDownloadTask *) downloadTaskWithResumeData: (NSData *) resumeData progress: (nullable void (^)(NSProgress *downloadProgress))downloadProgressBlock destination:(nullable NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination completionHandler:(nullable void (^)(NSURLResponse *response, NSURL * _Nullable filePath, NSError * _Nullable error))completionHandler;Copy the code
  • Gets the Tasks progress method
*/ - (nullable NSProgress *)uploadProgressForTask:(NSURLSessionTask *)task; /** Obtain the download progress of the specified task */ - (nullable NSProgress *)downloadProgressForTask (NSURLSessionTask *)task;Copy the code
  • Set the NSURLSessionDelegate callback method
/ * * set when the session is invalid block callback, the block back to call in NSURLSessionDelegate method URLSession: didBecomeInvalidWithError: * / - (void)setSessionDidBecomeInvalidBlock:(nullable void (^)(NSURLSession *session, NSError *error))block; /** Sets the block callback when the session receives an authentication request. The block back to call in NSURLSessionDelegate method URLSession: didReceiveChallenge: completionHandler: * / - (void)setSessionDidReceiveAuthenticationChallengeBlock:(nullable NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * _Nullable __autoreleasing * _Nullable credential))block;
Copy the code
  • Set the NSURLSessionTaskDelegate callback method
/ * * set when the task requires a new input stream block of callback, the block back to call in NSURLSessionTaskDelegate method URLSession: task: needNewBodyStream: * / - (void)setTaskNeedNewBodyStreamBlock:(nullable NSInputStream * (^)(NSURLSession *session, NSURLSessionTask *task))block; /** Sets the block callback when an HTTP request attempts to perform a redirect to a different URL, The block back to call in NSURLSessionTaskDelegate method URLSession: willPerformHTTPRedirection: newRequest: completionHandler: * / - (void)setTaskWillPerformHTTPRedirectionBlock:(nullable NSURLRequest * _Nullable (^)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request))block; /** Sets the block callback when task receives authentication, The block back to call in NSURLSessionTaskDelegate method URLSession: task: didReceiveChallenge: completionHandler: * / - (void)setTaskDidReceiveAuthenticationChallengeBlock:(nullable NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential * _Nullable __autoreleasing * _Nullable credential))block; /** Set a block callback to periodically track the upload progress, The block back to call in NSURLSessionTaskDelegate method URLSession: task: didSendBodyData: totalBytesSent: totalBytesExpectedToSend: * / - (void)setTaskDidSendBodyDataBlock:(nullable void (^)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend))block; / * * set when the task completes the block when the callback, the block back to call in NSURLSessionTaskDelegate method URLSession: task: didCompleteWithError: * / - (void)setTaskDidCompleteBlock:(nullable void (^)(NSURLSession *session, NSURLSessionTask *task, NSError * _Nullable error))block;
Copy the code
  • Sets the NSURLSessionDataDelegate callback method
/** Sets the block callback when the dataTask receives the response, The block back to call in NSURLSessionDataDelegate method URLSession: dataTask: didReceiveResponse: completionHandler: * / - (void)setDataTaskDidReceiveResponseBlock:(nullable NSURLSessionResponseDisposition (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response))block; /** Sets the block callback when the dataTask becomes downloadTask, The block back to call in NSURLSessionDataDelegate method URLSession: dataTask: didBecomeDownloadTask: * / - (void)setDataTaskDidBecomeDownloadTaskBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask))block; / * * set when dataTask receives the data block of the callback, the block back to call in NSURLSessionDataDelegate method URLSession: dataTask: didReceiveData: * / - (void)setDataTaskDidReceiveDataBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data))block; /** Sets the block callback when the dataTask will cache the response, The block back to call in NSURLSessionDataDelegate method URLSession: dataTask: willCacheResponse: completionHandler: * / - (void)setDataTaskWillCacheResponseBlock:(nullable NSCachedURLResponse * (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse))block; /** Sets the block callback when all messages in the session queue are sent. The block back to call in NSURLSessionDataDelegate method URLSessionDidFinishEventsForBackgroundURLSession: * / - (void)setDidFinishEventsForBackgroundURLSessionBlock:(nullable void (^)(NSURLSession *session))block;
Copy the code
  • Set the NSURLSessionDownloadDelegate callback methods
/** Sets the block callback when downloadTask completes a download, The block back to call in NSURLSessionDownloadDelegate method URLSession: downloadTask: didFinishDownloadingToURL: * / - (void)setDownloadTaskDidFinishDownloadingBlock:(nullable NSURL * _Nullable (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location))block; /** Set a block callback to periodically track download progress, The block back to call in NSURLSessionDownloadDelegate method URLSession: downloadTask: didWriteData: totalBytesWritten: totalBytesWritten: total BytesExpectedToWrite: Middle */ - (void)setDownloadTaskDidWriteDataBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite))block; /** Sets the block callback when downloadTask resumes downloading, The block back to call in NSURLSessionDownloadDelegate method URLSession: downloadTask: didResumeAtOffset: expectedTotalBytes: * / - (void)setDownloadTaskDidResumeBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t fileOffset, int64_t expectedTotalBytes))block;
Copy the code

1.3. Global static constants

  • Notice the name
/ * * when a task is to start with this notice sending * / FOUNDATION_EXPORT nsstrings * const AFNetworkingTaskDidResumeNotification; / * * when a task execution is finished it will send this notice * / FOUNDATION_EXPORT nsstrings * const AFNetworkingTaskDidCompleteNotification; / * * as a task to suspend it will send the notification * / FOUNDATION_EXPORT nsstrings * const AFNetworkingTaskDidSuspendNotification; / * * when a session is invalid will send this notice * / FOUNDATION_EXPORT nsstrings * const AFURLSessionDidInvalidateNotification; /** This notification is sent when sessionDownloadTask fails to move a file downloaded from a temporary path to a user-specified path */ FOUNDATION_EXPORT NSString * const AFURLSessionDownloadTaskDidFailToMoveFileNotification;Copy the code
  • Notify the key of the userInfo dictionary type data that is passed
/ * * from AFNetworkingTaskDidCompleteNotification notification by this key can be passed by the userInfo dictionary type data to retrieve the task response of raw data * / FOUNDATION_EXPORT nsstrings * const AFNetworkingTaskDidCompleteResponseDataKey; /** If the response has been serialized, From AFNetworkingTaskDidCompleteNotification notification by this key can be passed by the userInfo dictionary type data to retrieve the task response serialized data * / FOUNDATION_EXPORT nsstrings * const  AFNetworkingTaskDidCompleteSerializedResponseKey; /** If the response serializes the object, From AFNetworkingTaskDidCompleteNotification notification by this key can be passed by the userInfo dictionary type data to retrieve the task response serialized object * / FOUNDATION_EXPORT nsstrings * const AFNetworkingTaskDidCompleteResponseSerializerKey; /** If the corresponding data has been stored directly to disk, Through this key can be passed from AFNetworkingTaskDidCompleteNotification notice the userInfo dictionary type data to retrieve download data storage paths * / FOUNDATION_EXPORT nsstrings * const AFNetworkingTaskDidCompleteAssetPathKey; /** If there is an error,, From AFNetworkingTaskDidCompleteNotification notification by this key can be passed by the userInfo dictionary type data to retrieve related to task or response to serialize error * / FOUNDATION_EXPORT nsstrings  * const AFNetworkingTaskDidCompleteErrorKey;Copy the code

2. Implementation file

2.1. The macro definition

#ifndef NSFoundationVersionNumber_iOS_8_0
# define NSFoundationVersionNumber_With_Fixed_5871104061079552_bug 1140.11
#else
#define NSFoundationVersionNumber_With_Fixed_5871104061079552_bug NSFoundationVersionNumber_iOS_8_0
#endif
Copy the code

This macro NSFoundation version to determine the current version of the system, and in general is defined NSFoundationVersionNumber_With_Fixed_5871104061079552_bug iOS8.0

2.2. Static methods

/** Create a singleton serial queue for task */ static dispatch_queue_turl_session_manager_creation_queue() {
    static dispatch_queue_t af_url_session_manager_creation_queue;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        af_url_session_manager_creation_queue = dispatch_queue_create("com.alamofire.networking.session.manager.creation", DISPATCH_QUEUE_SERIAL);
    });

    returnaf_url_session_manager_creation_queue; } /** Create a task safely, mainly for compatibility with iOS8 system bugs, Static void url_session_manager_create_task_safely(dispatch_block_t) */ static void url_session_manager_create_task_safely(dispatch_block_t block) {if (NSFoundationVersionNumber < NSFoundationVersionNumber_With_Fixed_5871104061079552_bug) {
        // Fix of bug
        // Open Radar:http://openradar.appspot.com/radar?id=5871104061079552 (status: Fixed in iOS8)
        // Issue about:https://github.com/AFNetworking/AFNetworking/issues/2093
        dispatch_sync(url_session_manager_creation_queue(), block);
    } else{ block(); }} /** Create a one-way queue for processing returned data */ static dispatch_queue_turl_session_manager_processing_queue() {
    static dispatch_queue_t af_url_session_manager_processing_queue;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        af_url_session_manager_processing_queue = dispatch_queue_create("com.alamofire.networking.session.manager.processing", DISPATCH_QUEUE_CONCURRENT);
    });

    returnaf_url_session_manager_processing_queue; } /** Create a simple interest group for handling call-back. You can use dispatch_group_notify to monitor the completion of call-backurl_session_manager_completion_group() {
    static dispatch_group_t af_url_session_manager_completion_group;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        af_url_session_manager_completion_group = dispatch_group_create();
    });

    return af_url_session_manager_completion_group;
}
Copy the code

2.3. Global static constants

  • These are assignments to global static constants in the.h file
NSString * const AFNetworkingTaskDidResumeNotification = @"com.alamofire.networking.task.resume";
NSString * const AFNetworkingTaskDidCompleteNotification = @"com.alamofire.networking.task.complete";
NSString * const AFNetworkingTaskDidSuspendNotification = @"com.alamofire.networking.task.suspend";
NSString * const AFURLSessionDidInvalidateNotification = @"com.alamofire.networking.session.invalidate";
NSString * const AFURLSessionDownloadTaskDidFailToMoveFileNotification = @"com.alamofire.networking.session.download.file-manager-error";

NSString * const AFNetworkingTaskDidCompleteSerializedResponseKey = @"com.alamofire.networking.task.complete.serializedresponse";
NSString * const AFNetworkingTaskDidCompleteResponseSerializerKey = @"com.alamofire.networking.task.complete.responseserializer";
NSString * const AFNetworkingTaskDidCompleteResponseDataKey = @"com.alamofire.networking.complete.finish.responsedata";
NSString * const AFNetworkingTaskDidCompleteErrorKey = @"com.alamofire.networking.task.complete.error";
NSString * const AFNetworkingTaskDidCompleteAssetPathKey = @"com.alamofire.networking.task.complete.assetpath";
Copy the code
  • Name the lock object
static NSString * const AFURLSessionManagerLockName = @"com.alamofire.networking.session.manager.lock";
Copy the code
  • This is where.hFile explain attributes attemptsToRecreateUploadTasksForBackgroundSessions mentioned try three times
static NSUInteger const AFMaximumNumberOfAttemptsToRecreateBackgroundSessionUploadTask = 3;
Copy the code

2.4. The alias

  • These are all for.hAlias for the incoming block in the callback method set in the file
typedef void (^AFURLSessionDidBecomeInvalidBlock)(NSURLSession *session, NSError *error);
typedef NSURLSessionAuthChallengeDisposition (^AFURLSessionDidReceiveAuthenticationChallengeBlock)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential);

typedef NSURLRequest * (^AFURLSessionTaskWillPerformHTTPRedirectionBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request);
typedef NSURLSessionAuthChallengeDisposition (^AFURLSessionTaskDidReceiveAuthenticationChallengeBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential);
typedef void (^AFURLSessionDidFinishEventsForBackgroundURLSessionBlock)(NSURLSession *session);

typedef NSInputStream * (^AFURLSessionTaskNeedNewBodyStreamBlock)(NSURLSession *session, NSURLSessionTask *task);
typedef void (^AFURLSessionTaskDidSendBodyDataBlock)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend);
typedef void (^AFURLSessionTaskDidCompleteBlock)(NSURLSession *session, NSURLSessionTask *task, NSError *error);

typedef NSURLSessionResponseDisposition (^AFURLSessionDataTaskDidReceiveResponseBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response);
typedef void (^AFURLSessionDataTaskDidBecomeDownloadTaskBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask);
typedef void (^AFURLSessionDataTaskDidReceiveDataBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data);
typedef NSCachedURLResponse * (^AFURLSessionDataTaskWillCacheResponseBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse);

typedef NSURL * (^AFURLSessionDownloadTaskDidFinishDownloadingBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location);
typedef void (^AFURLSessionDownloadTaskDidWriteDataBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite);
typedef void (^AFURLSessionDownloadTaskDidResumeBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t fileOffset, int64_t expectedTotalBytes);
Copy the code
  • This is the alias for the progress callback block
typedef void (^AFURLSessionTaskProgressBlock)(NSProgress *);
Copy the code
  • This is the alias for the completion callback block
typedef void (^AFURLSessionTaskCompletionHandler)(NSURLResponse *response, id responseObject, NSError *error);
Copy the code

2.5. AFURLSessionManagerTaskDelegate private class

2.5.1. Interface

  • attribute
/** network session manager; /* @property (nonatomic, weak) AFURLSessionManager *manager; /** Save received data */ @property (nonatomic, strong) NSMutableData *mutableData; /** uploadProgress */ @property (nonatomic, strong) NSProgress *uploadProgress; /** downloadProgress */ @property (nonatomic, strong) NSProgress *downloadProgress; /** download save path */ @property (nonatomic, copy) NSURL *downloadFileURL; /** Save download complete callback block */ @property (nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading; * * save/upload progress callback block * / @ property (nonatomic, copy) AFURLSessionTaskProgressBlock uploadProgressBlock; / * * to save the download progress callback block * / @ property (nonatomic, copy) AFURLSessionTaskProgressBlock downloadProgressBlock; / save completion callback block * * * / @ property (nonatomic, copy) AFURLSessionTaskCompletionHandler completionHandler;Copy the code
  • methods
/** specifies the NSURLSessionTask initialization method */ - (instancetype)initWithTask:(NSURLSessionTask *)task;Copy the code

2.5.2. Achieve

  • Lifecycle approach
- (instancetype)initWithTask:(NSURLSessionTask *)task {
    self = [super init];
    if(! self) {returnnil; } // Initialize attribute _mutableData = [NSMutableData data]; _uploadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil]; _downloadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil]; __weak __typeof__(task) weakTask = task; // Bind the cancellation, pause, and restart of the incoming task to the corresponding operation of the progress object.for (NSProgress *progress in @[ _uploadProgress, _downloadProgress ])
    {
        progress.totalUnitCount = NSURLSessionTransferSizeUnknown;
        progress.cancellable = YES;
        progress.cancellationHandler = ^{
            [weakTask cancel];
        };
        progress.pausable = YES;
        progress.pausingHandler = ^{
            [weakTask suspend];
        };
#if __has_warning("-Wunguarded-availability-new")
        if (@available(iOS 9, macOS 10.11, *)) {
#else
        if ([progress respondsToSelector:@selector(setResumingHandler:)]) {
#endifprogress.resumingHandler = ^{ [weakTask resume]; }; } // Observe the progress of the progress object [progress addObserver:self]forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
                      options:NSKeyValueObservingOptionNew
                      context:NULL];
    }
    returnself; } - (void)dealloc {// Remove progress object observation [self.downloadProgress removeObserver:selfforKeyPath:NSStringFromSelector(@selector(fractionCompleted))];
    [self.uploadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))];
}
Copy the code
  • KVO method
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change Context :(void *)context {// when the progress object's progress changes, the corresponding block is called backif ([object isEqual:self.downloadProgress]) {
        if(self.downloadProgressBlock) { self.downloadProgressBlock(object); }}else if ([object isEqual:self.uploadProgress]) {
        if(self.uploadProgressBlock) { self.uploadProgressBlock(object); }}}Copy the code
  • NSURLSessionTaskDelegate method
/** When the NSURLSessionTaskDelegate method is called, it means that the task has finished executing, */ - (void)URLSession:(__unused NSURLSession *)session task:(NSURLSessionTask *)task DidCompleteWithError :(NSError *)error {// because self.manager is weak, __strong __strong AFURLSessionManager *manager = self.manager; __block __block id responseObject = nil; // Use __block to store the data passed when sending notifications. __block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer; // Use temporary variables to save the requested data and empty the attributes to save the data to save memory# 2672
    NSData *data = nil;
    if(self.mutableData) { data = [self.mutableData copy]; //We no longer need the reference, so nil it out to gain back some memory. self.mutableData = nil; } // If the save path of the downloaded file is set, the save path is passed, otherwise the requested data is passed, if there is requested dataif (self.downloadFileURL) {
        userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
    } else if(data) { userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data; } // If the request failsif(error) {/ / error message the userInfo [AFNetworkingTaskDidCompleteErrorKey] = error; Dispatch_group_async (manager.completionGroup?: Url_session_manager_completion_group (), manager.completionQueue?: dispatch_get_main_queue(), ^{// Callback and send notificationsif(self.completionHandler) { self.completionHandler(task.response, responseObject, error); } dispatch_async(dispatch_get_main_queue(), ^{ [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo]; }); }); // If the request succeeds}else{dispatch_async(url_session_manager_processing_queue(), ^{NSError *serializationError = nil; responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError]; // If there is a path to save the downloaded file, return the pathif(self.downloadFileURL) { responseObject = self.downloadFileURL; } // Pass the response serialized objectif(responseObject) { userInfo[AFNetworkingTaskDidCompleteSerializedResponseKey] = responseObject; } // Pass the error object if parsing failsif(serializationError) { userInfo[AFNetworkingTaskDidCompleteErrorKey] = serializationError; } // Dispatch_group_async (manager.completionGroup?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{if(self.completionHandler) { self.completionHandler(task.response, responseObject, serializationError); } dispatch_async(dispatch_get_main_queue(), ^{ [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo]; }); }); }); }}Copy the code
  • NSURLSessionDataDelegate method implementation
/** When the NSURLSessionDataDelegate method is called, */ - (void)URLSession:(__unused NSURLSession *)session dataTask:(__unused NSURLSessionDataTask *) dataTask didReceiveData: (NSData *) data {/ attributes of the object/download progress update self. The downloadProgress. TotalUnitCount = dataTask.countOfBytesExpectedToReceive; self.downloadProgress.completedUnitCount = dataTask.countOfBytesReceived; [self.mutableData appendData:data]; } /** When the NSURLSessionDataDelegate method is called, */ - (void)URLSession (NSURLSession *) SessionTask (NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent TotalBytesExpectedToSend: int64_t totalBytesExpectedToSend {/ attributes of the object/upload progress update self. The uploadProgress. TotalUnitCount = task.countOfBytesExpectedToSend; self.uploadProgress.completedUnitCount = task.countOfBytesSent; }Copy the code
  • NSURLSessionDownloadDelegate method implementation
When performing the duty of the download / * * and ` NSURLSessionDownloadDelegate ` regularly calls this method, Pass the download progress */ - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten TotalBytesExpectedToWrite: int64_t totalBytesExpectedToWrite {/ attributes of the object/download progress update self. The downloadProgress. TotalUnitCount = totalBytesExpectedToWrite; self.downloadProgress.completedUnitCount = totalBytesWritten; } /** When the NSURLSessionDataDelegate method is called, */ - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask DidResumeAtOffset :(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes{// updates the properties of the download progress object self.downloadProgress.totalUnitCount = expectedTotalBytes; self.downloadProgress.completedUnitCount = fileOffset; } /** When the NSURLSessionDataDelegate method is called, */ - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask DidFinishDownloadingToURL location: (NSURL *) {/ / if the user is set up to save the download file path, will download the files from the temporary path in the past, Send notification self.downloadFileURL = nil when movement is complete;if (self.downloadTaskDidFinishDownloading) {
        self.downloadFileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);
        if (self.downloadFileURL) {
            NSError *fileManagerError = nil;

            if(! [[NSFileManager defaultManager] moveItemAtURL:location toURL:self.downloadFileURL error:&fileManagerError]) { [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:fileManagerError.userInfo]; }}}}Copy the code

Thus, it can be seen that AFURLSessionManagerTaskDelegate this class is mainly used to monitor the progress of the upload and download and handling of data after the completion of the task and callback.

2.6. _AFURLSessionTaskSwizzling private class

2.6.1. Static methods

Static inline void af_swizzleSelector(Class theClass, SEL originalSelector, SEL swizzledSelector) { Method originalMethod = class_getInstanceMethod(theClass, originalSelector); Method swizzledMethod = class_getInstanceMethod(theClass, swizzledSelector); method_exchangeImplementations(originalMethod, swizzledMethod); } /** Add a Method to a Class */ static inline BOOL af_addMethod(Class theClass, SEL selector, Method Method) {return class_addMethod(theClass, selector,  method_getImplementation(method),  method_getTypeEncoding(method));
}
Copy the code

2.6.2. Static constants

/ * * when a task is to start with this notice sending * / static nsstrings * const AFNSURLSessionTaskDidResumeNotification = @"com.alamofire.networking.nsurlsessiontask.resume"; / * * as a task to suspend it will send the notification * / static nsstrings * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofire.networking.nsurlsessiontask.suspend";
Copy the code

2.6.3. Method implementation

+ (void)load { /** WARNING: Trouble Ahead https://github.com/AFNetworking/AFNetworking/pull/2702 * / / / determine whether NSURLSessionTask this classif (NSClassFromString(@"NSURLSessionTask") {/** NSURLSessionTask is implemented differently in iOS 7 and iOS 8, which makes the following code a bit tricky. Many unit tests have been done to verify the feasibility of this approach. -NSURLSessionTasks are implemented using the class cluster design pattern, which means that the class you get through the interface provided by this class is not that class. - Simply passing the '[NSURLSessionTask Class]' method doesn't work, you need to create a task with NSURLSession to get its class. - In iOS 7, 'localThe DataTask type is' __NSCFLocalDataTask ', and its inheritance is: __NSCFLocalDataTask -> __NSCFLocalSessionTask -> __NSCFURLSessionTask. - In iOS 8, 'localThe DataTask type is' __NSCFLocalDataTask ', and its inheritance is: __NSCFLocalDataTask -> __NSCFLocalSessionTask -> NSURLSessionTask. - In iOS 7, only '__NSCFLocalSessionTask' and its parent '__NSCFURLSessionTask' implement their 'resume' and 'suspendMethod, and the '__NSCFLocalSessionTask' class does not call its parent's methods in the method implementation, which means that the two classes have to swap methods. - In iOS 8, only the 'NSURLSessionTask' class implements' resume 'and'suspend'method, which means that only method swaps are performed on this class. - Since the 'NSURLSessionTask' class does not exist in every iOS version, it is easier to add and manage exchange methods in this fictional class with some assumptions: - 'resume' and 'Resume'suspendMethod is implemented without calling the implementation method of its parent class. However, if the implementation method of its parent class is called in a later version of iOS, we have to deal with it again. - "resume" and"suspendThe '__NSCFLocalDataTask' method will not be copied by other classes: 1) instantiate a dataTask from 'NSURLSession' and get the '__NSCFLocalDataTask' from the dataTask. 2) Get a pointer to the original implementation of the 'af_resume' method. 4) Get the parent of the current class. 5) Get the pointer to the current class that points to the implementation of the 'Resume' method. 6) Get a pointer to the parent of the current class to the implementation of the 'Resume' method. 7) If the current class and its parent have different Pointers to the implementation of the 'resume' method, and the current class has different Pointers to the implementation of the 'resume' method and the original implementation of the 'AF_resume' method, Exchange method is 8) and then through step 3 to 8 check its parent class * / / / instantiate the network session configuration object NSURLSessionConfiguration * configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration]; / / instantiate the network session object NSURLSession * session = [NSURLSession sessionWithConfiguration: configuration];#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wnonnull"// Instantiate the network session task object NSURLSessionDataTask *localDataTask = [session dataTaskWithURL:nil];
#pragma clang diagnostic popIMP originalAFResumeIMP = method_getImplementation(class_getInstanceMethod([self class], @selector(af_resume))); Class currentClass = [localDataTask class]; If the concrete class implements the resume method, it enters the body of the loopwhile(currentClass, @selector(resume)) {class_getInstanceMethod(currentClass, @selector(resume));  IMP classResumeIMP = method_getImplementation(class_getInstanceMethod(currentClass, @selector(resume))); IMP superclassResumeIMP = method_getImplementation(class_getInstanceMethod(superClass, @selector(resume))); // If the pointer to the resume implementation of the concrete class is inconsistent with the pointer to the resume implementation of its parent class and the pointer to the original implementation of the af_resume method of the current classif(classResumeIMP ! = superclassResumeIMP && originalAFResumeIMP ! = classResumeIMP) {// Add the af_resume and af_suspend methods to the current concrete class and resume andsuspendMethods exchange [self swizzleResumeAndSuspendMethodForClass: currentClass]; CurrentClass = [currentClass superclass]; } // Cancel the network session task [localDataTask cancel]; // Terminate the network session immediately [session finishTasksAndInvalidate]; }} /** dynamically adds the af_resume and af_suspend methods to a resume and suspend methodsuspendExchange * / + (void) swizzleResumeAndSuspendMethodForClass theClass: (Class) {/ / for this Class of af_resume and af_suspend Method afResumeMethod = class_getInstanceMethod(self, @selector(af_resume)); Method afSuspendMethod = class_getInstanceMethod(self, @selector(af_suspend)); // If you want to successfully add the af_resume method to the target classif(af_addMethod(theClass, @selector(af_resume), // Exchange target class resume with af_resume method af_swizzleSelector(theClass, @selector(resume), @selector(af_resume)); } // If you want to successfully add the af_suspend method to the target classif(af_addMethod(theClass, @selector(af_suspend), afSuspendMethod)) {// Exchange the target classsuspendAnd the af_suspend method af_swizzleSelector(theClass, @selector(suspend), @selector(af_suspend));
    }
}

- (NSURLSessionTaskState)state {
    NSAssert(NO, @"State method should never be called in the actual dummy class");
    returnNSURLSessionTaskStateCanceling; } - (void)af_resume {// Get the state property of NSURLSessionDataTask, And call its resume method NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state"); NSURLSessionTaskState state = [self state]; [self af_resume]; // If the state before calling the restart method is not healthy, a notification is sentif(state ! = NSURLSessionTaskStateRunning) { [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidResumeNotification object:self]; }} - (void)af_suspend {// Get the state property of NSURLSessionDataTask and call itsuspendMethod NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state"); NSURLSessionTaskState state = [self state]; [self af_suspend]; If the state before calling the pause method is not paused, a notification is sentif (state != NSURLSessionTaskStateSuspended) {
        [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidSuspendNotification object:self];
    }
}
Copy the code

After reading the private class, found a problem: is _AFURLSessionTaskSwizzling hasn’t been called, even did not appear in any parts of the other code, this is very strange. How does writing all this code work if there’s no call? The load function is automatically called once for each class, regardless of whether the class is used or not. If you want to learn more about the load method, check out this article: Load and Initialize on iOS. Do you really understand the Load method?

2.7 class extensions

/ * * save network session configuration object * / @ property (readwrite nonatomic, strong) NSURLSessionConfiguration * sessionConfiguration; /** save callback operationQueue object */ @property (readwrite, nonatomic, strong) NSOperationQueue *operationQueue; */ @property (readWrite, nonatomic, strong) NSURLSession *session; / * * save AFURLSessionManagerTaskDelegate the connection between the object and task * / @ property (readwrite, nonatomic. strong) NSMutableDictionary *mutableTaskDelegatesKeyedByTaskIdentifier; /** The taskDescription property value of the task is set to the address of the current object */ @property (readonly, nonatomic, copy) NSString *taskDescriptionForSessionTasks; /** Save lock object, This lock is used to secure access to mutableTaskDelegatesKeyedByTaskIdentifier thread * / @ property (readwrite nonatomic, strong) NSLock * lock; /** Save block callback for invalid session */ @property (readwrite, nonatomic, copy) AFURLSessionDidBecomeInvalidBlock sessionDidBecomeInvalid; /** Save block callback when session receives validation request */ @property (readwrite, nonatomic, copy) AFURLSessionDidReceiveAuthenticationChallengeBlock sessionDidReceiveAuthenticationChallenge; /** Save block callback when all messages in session queue are sent */ @property (readwrite, nonatomic, copy) AFURLSessionDidFinishEventsForBackgroundURLSessionBlock didFinishEventsForBackgroundURLSession; /** Save a block callback when an HTTP request tries to perform a redirect to a different URL */ @property (readwrite, nonatomic, copy) AFURLSessionTaskWillPerformHTTPRedirectionBlock taskWillPerformHTTPRedirection; /** Save block callback when task receives authentication */ @property (readwrite, nonatomic, copy) AFURLSessionTaskDidReceiveAuthenticationChallengeBlock taskDidReceiveAuthenticationChallenge; /** Save block callback when task needs a new input stream */ @property (readwrite, nonatomic, copy) AFURLSessionTaskNeedNewBodyStreamBlock taskNeedNewBodyStream; /** Save a block callback to periodically track upload progress */ @property (readwrite, nonatomic, copy) AFURLSessionTaskDidSendBodyDataBlock taskDidSendBodyData; / * * save task completes the block when the callback * / @ property (readwrite nonatomic, copy) AFURLSessionTaskDidCompleteBlock taskDidComplete; /** Save the block callback when dataTask receives the response */ @property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidReceiveResponseBlock dataTaskDidReceiveResponse; /** Save block callback when dataTask becomes downloadTask */ @property (readWrite, nonatomic, copy) AFURLSessionDataTaskDidBecomeDownloadTaskBlock dataTaskDidBecomeDownloadTask; /** Save block callback when dataTask receives data */ @property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidReceiveDataBlock dataTaskDidReceiveData; /** Save block callback when dataTask will cache the response */ @property (readwrite, nonatomic, copy) AFURLSessionDataTaskWillCacheResponseBlock dataTaskWillCacheResponse; /** Save downloadTask to complete a download block callback */ @property (readWrite, nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading; /** Save a block callback to periodically track download progress */ @property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidWriteDataBlock downloadTaskDidWriteData; /** save the block callback to restart the downloadTask */ @property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidResumeBlock downloadTaskDidResume;Copy the code

2.8. Method implementation

  • Lifecycle approach
- (instanceType)init {// The default configuration is initializedreturn [self initWithSessionConfiguration:nil];
}

- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
    self = [super init];
    if(! self) {returnnil; } // The default network session configuration is used if no configuration object is passed inif(! configuration) { configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; } self.sessionConfiguration = configuration; // instantiate an operationQueue and set the maximum concurrency to 1 self.operationQueue = [[NSOperationQueue alloc] init]; self.operationQueue.maxConcurrentOperationCount = 1; / / generated by the two objects instantiated network session object self. The session = [NSURLSession sessionWithConfiguration: self. SessionConfiguration delegate: self delegateQueue:self.operationQueue]; // the default responseSerializer is the serialized json data object self.responseSerializer = [AFJSONResponseSerializer]; // the default securityPolicy is the default securityPolicy self.securitypolicy = [AFSecurityPolicy defaultPolicy]; // Instantiate the network status monitoring object#if ! TARGET_OS_WATCH
    self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
#endif/ / instantiate the variable dictionary associated task and its AFURLSessionManagerTaskDelegate object, Make a corresponding one self. MutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init]; Self. lock = [[NSLock alloc] init]; self.lock = [NSLock alloc] init]; self.lock.name = AFURLSessionManagerLockName; // Asynchronously retrieve all pending tasks of the current session and empty them. Mainly to switch from the background to the front desk to initialize the session when [the self. The session getTasksWithCompletionHandler: ^ (NSArray * dataTasks, NSArray * uploadTasks, NSArray *downloadTasks) {for (NSURLSessionDataTask *task in dataTasks) {
            [self addDelegateForDataTask:task uploadProgress:nil downloadProgress:nil completionHandler:nil];
        }

        for (NSURLSessionUploadTask *uploadTask in uploadTasks) {
            [self addDelegateForUploadTask:uploadTask progress:nil completionHandler:nil];
        }

        for (NSURLSessionDownloadTask *downloadTask indownloadTasks) { [self addDelegateForDownloadTask:downloadTask progress:nil destination:nil completionHandler:nil]; }}];returnself; } - (void)dealloc {// Remove the notification observer [[NSNotificationCenter defaultCenter] removeObserver:self]; }Copy the code
  • Lazy loading method
/** Get the address string of the current AFURLSessionManager object assigned to the taskDescription property of the task, Purpose is by giving notice to listening to the string to determine whether the current task AFURLSessionManager objects have a * / - (nsstrings *) taskDescriptionForSessionTasks {return [NSString stringWithFormat:@"%p", self];
}
Copy the code
  • Notification response method
/** this method is called by notification when the task has restarted */ - (void)taskDidResume:(NSNotification *)notification {// the notification object obtains the task that sent the notification NSURLSessionTask *task = notification.object; // If the task belongs to the AFURLSessionManager objectif ([task respondsToSelector:@selector(taskDescription)]) {
        if([task. TaskDescription isEqualToString: self taskDescriptionForSessionTasks]) {/ / main thread asynchronous notifications task has been restarted dispatch_async(dispatch_get_main_queue(), ^{ [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidResumeNotification object:task]; }); }}} /** taskDidSuspend:(NSNotification *)notification {// get the task that sent the notification from the notification object NSURLSessionTask *task = notification.object; // If the task belongs to the AFURLSessionManager objectif ([task respondsToSelector:@selector(taskDescription)]) {
        if([task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks]) { dispatch_async(dispatch_get_main_queue(), ^{// Main thread asynchronously sending notification task has paused [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidSuspendNotification object:task]; }); }}}Copy the code
  • Private methods dealing AFURLSessionManagerTaskDelegate object and NSURLSessionTask object relations
/ * * * through a task object for its binding AFURLSessionManagerTaskDelegate object / - (AFURLSessionManagerTaskDelegate *)delegateForTask:(NSURLSessionTask *)task {// in debug, crash NSParameterAssert(task); // In a thread-safe environment, using the taskIdentifier attribute of the task, Obtain the binding delegate from mutableTaskDelegatesKeyedByTaskIdentifier attribute AFURLSessionManagerTaskDelegate * delegate = nil; [self.lock lock]; delegate = self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)]; [self.lock unlock];returndelegate; } /** bind task delegate */ - (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
            forTask:(NSURLSessionTask *) Task {// in debug, crash NSParameterAssert(Task); NSParameterAssert(delegate); // In thread-safe environment [self.lock lock]; / / task taskIdentifier attributes as key, is the value with the delegate, save to mutableTaskDelegatesKeyedByTaskIdentifier attribute. self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate; / / add notification to monitor task restart and suspend events [self addNotificationObserverForTask: task]; [self.lock unlock]; } /** bind delegate */ - (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 {// Instantiate the delegate object by dataTask, And initialize the properties AFURLSessionManagerTaskDelegate * delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask: dataTask]; delegate.manager = self; delegate.completionHandler = completionHandler; / / tag dataTask belong AFURLSessionManager object dataTask. TaskDescription = self. TaskDescriptionForSessionTasks; // Bind delegate [self to the dataTasksetDelegate:delegate forTask:dataTask]; delegate.uploadProgressBlock = uploadProgressBlock; delegate.downloadProgressBlock = downloadProgressBlock; } / delegate * * * for uploadTask binding / - (void) addDelegateForUploadTask: (NSURLSessionUploadTask *) uploadTask progress: (void (^)(NSProgress *uploadProgress)) uploadProgressBlock completionHandler:(void (^)(NSURLResponse *response, Id responseObject, NSError *error))completionHandler {// Instantiate the delegate object via uploadTask, And initialize the properties AFURLSessionManagerTaskDelegate * delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask: uploadTask]; delegate.manager = self; delegate.completionHandler = completionHandler; / / tag uploadTask belong AFURLSessionManager object uploadTask. TaskDescription = self. TaskDescriptionForSessionTasks; // Bind the uploadTask delegate [self]setDelegate:delegate forTask:uploadTask]; delegate.uploadProgressBlock = uploadProgressBlock; } / delegate * * * for downloadTask binding / - (void) addDelegateForDownloadTask: (downloadTask NSURLSessionDownloadTask *) progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler {// Instantiate the delegate object via downloadTask, And initialize the properties AFURLSessionManagerTaskDelegate * delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask: downloadTask]; delegate.manager = self; delegate.completionHandler = completionHandler; // If you set a path to save the downloaded file, assign it to the delegateif (destination) {
        delegate.downloadTaskDidFinishDownloading = ^NSURL * (NSURLSession * __unused session, NSURLSessionDownloadTask *task, NSURL *location) {
            returndestination(location, task.response); }; } / / tag downloadTask belong AFURLSessionManager object downloadTask. TaskDescription = self. TaskDescriptionForSessionTasks; // Bind delegate [self to the downloadTasksetDelegate:delegate forTask:downloadTask]; delegate.downloadProgressBlock = downloadProgressBlock; } /** remove task delegate */ - (void)removeDelegateForTask:(NSURLSessionTask *)task { Crash NSParameterAssert(Task) without arguments; // In thread-safe environment [self.lock lock]; / / remove the task observation notice [self removeNotificationObserverForTask: task]; / / removed from mutableTaskDelegatesKeyedByTaskIdentifier task [self mutableTaskDelegatesKeyedByTaskIdentifier removeObjectForKey:@(task.taskIdentifier)]; [self.lock unlock]; }Copy the code
  • Public interface gets the GET method for task-related attributes
/** retrieve tasks */ - (NSArray *)tasksForKeyPath:(NSString *)keyPath {// create a temporary variable to hold the retrieved tasks __block NSArray *tasks = nil; / / create a semaphore, because getTasksWithCompletionHandler this method is asynchronous tasks, in order to avoid haven't access to the tasks is returned, Dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {// Get tasks based on the names of the different types of tasks that are passed inif ([keyPath isEqualToString:NSStringFromSelector(@selector(dataTasks))]) {
            tasks = dataTasks;
        } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(uploadTasks))]) {
            tasks = uploadTasks;
        } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(downloadTasks))]) {
            tasks = downloadTasks;
        } else if([keyPath isEqualToString: NSStringFromSelector (@ the selector (tasks)]) {/ / use valueForKeyPath merger array and retain duplicate values the tasks = [@[dataTasks, uploadTasks, downloadTasks] valueForKeyPath:@"@unionOfArrays.self"]; } dispatch_semaphore_signal(semaphore); }]; Dispatch_semaphore_wait (semaphore, DISPATCH_TIME_FOREVER);returntasks; } /** Get all tasks of the current AFURLSessionManager object */ - (NSArray *)tasks {return[self tasksForKeyPath:NSStringFromSelector(_cmd)]; } /** Get the current AFURLSessionManager object dataTasks */ - (NSArray *)dataTasks {return[self tasksForKeyPath:NSStringFromSelector(_cmd)]; } /** Get all uploadTasks for the current AFURLSessionManager */ - (NSArray *)uploadTasks {return[self tasksForKeyPath:NSStringFromSelector(_cmd)]; } /** Get all downloadTasks for the current AFURLSessionManager object */ - (NSArray *)downloadTasks {return [self tasksForKeyPath:NSStringFromSelector(_cmd)];
}
Copy the code

By implementing the tasksForKeyPath: method, we can learn how to use GCD’s semaphore to convert asynchronous callbacks to synchronous execution

  • Public interface ends a network session
/** When YES is passed, the current network session is closed immediately; When the NO, wait for after completion of the task to close the current dialog * / - (void) invalidateSessionCancelingTasks: (BOOL) cancelPendingTasks {if (cancelPendingTasks) {
        [self.session invalidateAndCancel];
    } else{ [self.session finishTasksAndInvalidate]; }}Copy the code
  • Public interface Sets the set method for the responseSerializer property
- (void)setResponseSerializer ResponseSerializer: (id < AFURLResponseSerialization >) {/ / in the debug, Crash NSParameterAssert(responseSerializer); // Save the responseSerializer = responseSerializer; }Copy the code
  • Private methods that handle tasks and their notifications
/ * * add observation notice * / to the task - (void) addNotificationObserverForTask: (NSURLSessionTask *) task {[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidResume:) name:AFNSURLSessionTaskDidResumeNotification object:task]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidSuspend:) name:AFNSURLSessionTaskDidSuspendNotification object:task]; } / remove task observation notice * * * / - (void) removeNotificationObserverForTask: (NSURLSessionTask *) task {[[NSNotificationCenter defaultCenter] removeObserver:self name:AFNSURLSessionTaskDidSuspendNotification object:task]; [[NSNotificationCenter defaultCenter] removeObserver:self name:AFNSURLSessionTaskDidResumeNotification object:task]; }Copy the code

At the private class before seen _AFURLSessionTaskSwizzling, AFNetworking exchanged task resume and suspend method.

Take resume method as an example to illustrate its internal implementation. When a task’s resume method is called, af_resume method is actually called.

In af_resume approach, af_resume method is called, is actually call system resume method, at the same time the notice sending AFNSURLSessionTaskDidResumeNotification.

The current AFURLSessionManager object calls the taskDidResume: method after receiving the notification by observing it.

In the method taskDidResume: judge trigger resume method is the task of the current object AFURLSessionManager holdings, in the main thread asynchronous send AFNetworkingTaskDidResumeNotification notice, Tell the outside world that the task has been restarted.

Through this series of processing, it can be seen that what AFNetworking wants to achieve is to add the function of sending out notifications to the original method, so as to achieve the purpose of monitoring. However, the internal implementation of NSURLSessionTask on different versions of the system and the design pattern of the class cluster caused it to be “a bit tricky”.

  • Creates a public method of the NSURLSessionDataTask object
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLResponse Response, id responseObject, NSError *error))completionHandler {// Call the following methodreturn[self dataTaskWithRequest:request uploadProgress:nil downloadProgress:nil completionHandler:completionHandler]; } - (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 {// Safely create a dataTask __block NSURLSessionDataTask *dataTask = from the incoming request nil; url_session_manager_create_task_safely(^{ dataTask = [self.session dataTaskWithRequest:request]; }); / / as AFURLSessionManagerTaskDelegate dataTask binding object, To monitor dataTask processing progress and processing data [self addDelegateForDataTask: dataTask uploadProgress: uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];return dataTask;
}
Copy the code
  • Creates a public method of the NSURLSessionUploadTask object
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler {// Securely create a uploadTask __block NSURLSessionUploadTask *uploadTask from the incoming request and fileURL = nil; url_session_manager_create_task_safely(^{ uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL]; / / the failure might create iOS7 / / uploadTask may be nil on iOS7 because uploadTaskWithRequest: fromFile: mayreturn nil despite being documented as nonnull (https://devforums.apple.com/message/926113# 926113)/ / if you don't have to create success, and allowed to create, and NSURLSessionConfiguration object is through backgroundSessionConfigurationWithIdentifier: method to create, just repeat try three timesif(! uploadTask && self.attemptsToRecreateUploadTasksForBackgroundSessions && self.session.configuration.identifier) {for(NSUInteger attempts = 0; ! uploadTask && attempts < AFMaximumNumberOfAttemptsToRecreateBackgroundSessionUploadTask; attempts++) { uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL]; }}}); / / as AFURLSessionManagerTaskDelegate uploadTask binding objects to monitor uploadTask processing progress and process the dataif (uploadTask) {
        [self addDelegateForUploadTask:uploadTask
                              progress:uploadProgressBlock
                     completionHandler:completionHandler];
    }

    returnuploadTask; } - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromData:(NSData *)bodyData progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler {// Securely create a uploadTask __block NSURLSessionUploadTask *uploadTask from incoming Request and bodyData  = nil; url_session_manager_create_task_safely(^{ uploadTask = [self.session uploadTaskWithRequest:request fromData:bodyData]; }); / / as AFURLSessionManagerTaskDelegate uploadTask binding object, To monitor uploadTask processing progress and processing data [self addDelegateForUploadTask: uploadTask progress: uploadProgressBlock completionHandler:completionHandler];returnuploadTask; } - (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler {// Securely create a uploadTask from the incoming request __block NSURLSessionUploadTask *uploadTask = nil; url_session_manager_create_task_safely(^{ uploadTask = [self.session uploadTaskWithStreamedRequest:request]; }); / / as AFURLSessionManagerTaskDelegate uploadTask binding object, To monitor uploadTask processing progress and processing data [self addDelegateForUploadTask: uploadTask progress: uploadProgressBlock completionHandler:completionHandler];return uploadTask;
}
Copy the code
  • Creates a public method of the NSURLSessionDownloadTask object
- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler {// Safely create a downloadTask from the incoming request nil; url_session_manager_create_task_safely(^{ downloadTask = [self.session downloadTaskWithRequest:request]; }); / / as AFURLSessionManagerTaskDelegate downloadTask binding object, To monitor downloadTask processing progress and processing data [self addDelegateForDownloadTask: downloadTask progress: downloadProgressBlock destination:destination completionHandler:completionHandler];returndownloadTask; } - (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler {// Safely restart a downloadTask with the passed resumeData. __block NSURLSessionDownloadTask *downloadTask  = nil; url_session_manager_create_task_safely(^{ downloadTask = [self.session downloadTaskWithResumeData:resumeData]; }); / / as AFURLSessionManagerTaskDelegate downloadTask binding object, To monitor downloadTask processing progress and processing data [self addDelegateForDownloadTask: downloadTask progress: downloadProgressBlock destination:destination completionHandler:completionHandler];return downloadTask;
}
Copy the code
  • Gets the public method of the Tasks progress method
- (NSProgress *)uploadProgressForTask:(NSURLSessionTask *)task {
    return [[self delegateForTask:task] uploadProgress];
}

- (NSProgress *)downloadProgressForTask:(NSURLSessionTask *)task {
    return [[self delegateForTask:task] downloadProgress];
}
Copy the code

These two methods are first by passing in the task of acquiring parameters to the binding AFURLSessionManagerTaskDelegate object, through AFURLSessionManagerTaskDelegate object access to its to monitor the progress of the data

  • Sets the public method of the NSURLSessionDelegate callback
- (void)setSessionDidBecomeInvalidBlock:(void (^)(NSURLSession *session, NSError *error))block {
    self.sessionDidBecomeInvalid = block;
}

- (void)setSessionDidReceiveAuthenticationChallengeBlock:(NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential))block {
    self.sessionDidReceiveAuthenticationChallenge = block;
}
Copy the code
  • Sets the public method of the NSURLSessionTaskDelegate callback
- (void)setTaskNeedNewBodyStreamBlock:(NSInputStream * (^)(NSURLSession *session, NSURLSessionTask *task))block {
    self.taskNeedNewBodyStream = block;
}

- (void)setTaskWillPerformHTTPRedirectionBlock:(NSURLRequest * (^)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request))block {
    self.taskWillPerformHTTPRedirection = block;
}

- (void)setTaskDidReceiveAuthenticationChallengeBlock:(NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential))block {
    self.taskDidReceiveAuthenticationChallenge = block;
}

- (void)setTaskDidSendBodyDataBlock:(void (^)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend))block {
    self.taskDidSendBodyData = block;
}

- (void)setTaskDidCompleteBlock:(void (^)(NSURLSession *session, NSURLSessionTask *task, NSError *error))block {
    self.taskDidComplete = block;
}
Copy the code
  • Public method to set the NSURLSessionDataDelegate callback
- (void)setDataTaskDidReceiveResponseBlock:(NSURLSessionResponseDisposition (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response))block {
    self.dataTaskDidReceiveResponse = block;
}

- (void)setDataTaskDidBecomeDownloadTaskBlock:(void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask))block {
    self.dataTaskDidBecomeDownloadTask = block;
}

- (void)setDataTaskDidReceiveDataBlock:(void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data))block {
    self.dataTaskDidReceiveData = block;
}

- (void)setDataTaskWillCacheResponseBlock:(NSCachedURLResponse * (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse))block {
    self.dataTaskWillCacheResponse = block;
}

- (void)setDidFinishEventsForBackgroundURLSessionBlock:(void (^)(NSURLSession *session))block {
    self.didFinishEventsForBackgroundURLSession = block;
}
Copy the code
  • Set the NSURLSessionDownloadDelegate callback public methods
- (void)setDownloadTaskDidFinishDownloadingBlock:(NSURL * (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location))block {
    self.downloadTaskDidFinishDownloading = block;
}

- (void)setDownloadTaskDidWriteDataBlock:(void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite))block {
    self.downloadTaskDidWriteData = block;
}

- (void)setDownloadTaskDidResumeBlock:(void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t fileOffset, int64_t expectedTotalBytes))block {
    self.downloadTaskDidResume = block;
}
Copy the code
  • NSObject methods
- (NSString *)description {// Customize print datareturn [NSString stringWithFormat:@"<%@: %p, session: %@, operationQueue: %@>", NSStringFromClass([self class]), self, self.session, self.operationQueue]; } - (BOOL)respondsToSelector:(SEL)selector {// if the corresponding block is not assigned, the corresponding proxy is not calledif (selector == @selector(URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:)) {
        returnself.taskWillPerformHTTPRedirection ! = nil; }else if (selector == @selector(URLSession:dataTask:didReceiveResponse:completionHandler:)) {
        returnself.dataTaskDidReceiveResponse ! = nil; }else if (selector == @selector(URLSession:dataTask:willCacheResponse:completionHandler:)) {
        returnself.dataTaskWillCacheResponse ! = nil; }else if (selector == @selector(URLSessionDidFinishEventsForBackgroundURLSession:)) {
        returnself.didFinishEventsForBackgroundURLSession ! = nil; }return [[self class] instancesRespondToSelector:selector];
}
Copy the code
  • NSURLSessionDelegate method implementation
/** If session is invalid, Will call the agent method * / - (void) URLSession: (NSURLSession *) session didBecomeInvalidWithError error: (NSError *) {/ / calls block the callback dataif(self.sessionDidBecomeInvalid) { self.sessionDidBecomeInvalid(session, error); } / / notifications [[NSNotificationCenter defaultCenter] postNotificationName: AFURLSessionDidInvalidateNotification object:session]; } /** When the session receives an authentication request, Will call the agent method * / - (void) URLSession: (NSURLSession *) session didReceiveChallenge challenge: (NSURLAuthenticationChallenge *) completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential completionHandler * the credential)) {/ / set a temporary variable to hold the data NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling; __block NSURLCredential *credential = nil; // If the certificate handling method is passed through the block callback, the argument is passed directlyif(self.sessionDidReceiveAuthenticationChallenge) { disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential); // If no certificate is passed in}else{/ / if the validation way for NSURLAuthenticationMethodServerTrustif([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { // If it is validated by the current class security policyif ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain: challenge. ProtectionSpace. Host]) {/ / generates the credential certificate = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; // The specified certificate is used if there is a certificate, otherwise the default is usedif (credential) {
                    disposition = NSURLSessionAuthChallengeUseCredential;
                } else{ disposition = NSURLSessionAuthChallengePerformDefaultHandling; } // If it is not validated by the current class security policy}else{/ / no need to verify certificate disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge; } / / if not NSURLAuthenticationMethodServerTrust validation way, also use the default mode verification certificate}else{ disposition = NSURLSessionAuthChallengePerformDefaultHandling; }} // Callback resultif(completionHandler) { completionHandler(disposition, credential); }}Copy the code
  • NSURLSessionTaskDelegate method
/** When an HTTP request attempts to perform a redirect to a different URL, */ - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest *))completionHandler { NSURLRequest *redirectRequest = request; // If a redirection is passed through a block, the call is passedif(self.taskWillPerformHTTPRedirection) { redirectRequest = self.taskWillPerformHTTPRedirection(session, task, response, request); } // Callback resultif(completionHandler) { completionHandler(redirectRequest); }} /** This proxy method is called when task receives authentication. This certificate validation is handled exactly the same way as the one above, except that the one above is session level, */ - (void)URLSession (NSURLSession *) SessionTask (NSURLSessionTask *) Task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential completionHandler * the credential)) {/ / set a temporary variable to hold the data NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling; __block NSURLCredential *credential = nil; // If the certificate processing method is passed through the block, it is called directlyif(self.taskDidReceiveAuthenticationChallenge) { disposition = self.taskDidReceiveAuthenticationChallenge(session, task, challenge, &credential); // If no certificate is passed in}else{/ / if the validation way for NSURLAuthenticationMethodServerTrustif([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { // If it is validated by the current class security policyif ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain: challenge. ProtectionSpace. Host]) {/ / set the authentication mode to by specifying the certificate verification, and generate a certificate of disposition = NSURLSessionAuthChallengeUseCredential;  credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; // If not verified by the current class security policy}else{/ / no need to verify certificate disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge; } / / if not NSURLAuthenticationMethodServerTrust validation way, way is using the default authentication certificate}else{ disposition = NSURLSessionAuthChallengePerformDefaultHandling; }} // Callback resultif(completionHandler) { completionHandler(disposition, credential); }} /** When task needs a new input stream, The proxy method is called */ - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task needNewBodyStream:(void (^)(NSInputStream *bodyStream))completionHandler {// Create a temporary variable to save data NSInputStream *inputStream = nil; // If a block callback is passed in to get an input stream, call itif(self.taskNeedNewBodyStream) { inputStream = self.taskNeedNewBodyStream(session, task); // If the task has an original input stream, use the original input stream}else if(task.originalRequest.HTTPBodyStream && [task.originalRequest.HTTPBodyStream conformsToProtocol:@protocol(NSCopying)]) {  inputStream = [task.originalRequest.HTTPBodyStream copy]; } // Callback resultif(completionHandler) { completionHandler(inputStream); }} /** When the task has already sent data, */ - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent TotalBytesSent: int64_t totalBytesSent totalBytesExpectedToSend: int64_t totalBytesExpectedToSend {/ / create a temporary variable to hold the data int64_t totalUnitCount = totalBytesExpectedToSend; // If the total size of the data fails to be obtainedif(totalUnitCount = = NSURLSessionTransferSizeUnknown) {/ / by "the Content - Length" field in the request header size nsstrings * contentLength = [task.originalRequest valueForHTTPHeaderField:@"Content-Length"];
        if(contentLength) { totalUnitCount = (int64_t) [contentLength longLongValue]; }} / / for the delegate object and task binding AFURLSessionManagerTaskDelegate * delegate = [self delegateForTask: task]; // Call the same delegate method implemented by the delegate object to listen for progressif(delegate) { [delegate URLSession:session task:task didSendBodyData:bytesSent totalBytesSent:totalBytesSent totalBytesExpectedToSend:totalBytesExpectedToSend]; } // Call block to call back dataif(self.taskDidSendBodyData) { self.taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalUnitCount); }} /** When the task is complete, This proxy method is called */ - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError Error *) {/ / for the delegate object and task binding AFURLSessionManagerTaskDelegate * delegate = [self delegateForTask: task]; // Delegate may be nil when completing a task wellin the background
    if(delegate) {/ / call the delegate object is implemented by the same agent method to process the results [delegate URLSession: session task: task didCompleteWithError: error]; / / remove the delegate of task to monitor the notification and its binding [self removeDelegateForTask: task]; } // Call block to call back dataif(self.taskDidComplete) { self.taskDidComplete(session, task, error); }}Copy the code
  • NSURLSessionDataDelegate method implementation
/** When the dataTask receives the response, */ - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition CompletionHandler disposition)) {/ / create a temporary variable to hold the received data after the processing pattern NSURLSessionResponseDisposition disposition = NSURLSessionResponseAllow; // If a processing method is passed through the block, it is called by passing the argumentif(self.dataTaskDidReceiveResponse) { disposition = self.dataTaskDidReceiveResponse(session, dataTask, response); } // Callback resultif(completionHandler) { completionHandler(disposition); }} /** When a dataTask becomes a downloadTask, */ - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask DidBecomeDownloadTask :(NSURLSessionDownloadTask *)downloadTask {// get the delegate object bound to the dataTask AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];if(delegate) {/ / the delegate from dataTask over tied, and then bind to downloadTask [self removeDelegateForTask: dataTask]; [selfsetDelegate:delegate forTask:downloadTask]; } // Call block to call back dataif(self.dataTaskDidBecomeDownloadTask) { self.dataTaskDidBecomeDownloadTask(session, dataTask, downloadTask); }} /** When dataTask receives data, This proxy method is called */ - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *) data {/ / gain and dataTask binding the delegate object AFURLSessionManagerTaskDelegate * delegate = [self delegateForTask: dataTask]; // Call the same delegate method implemented by the delegate object to handle the result [Delegate URLSession: Session dataTask:dataTask didReceiveData:data]; // Call block to call back dataif(self.dataTaskDidReceiveData) { self.dataTaskDidReceiveData(session, dataTask, data); }} /** When the dataTask is about to cache the response, */ - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask willCacheResponse:(NSCachedURLResponse *)proposedResponse completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler {// Create a temporary variable to hold the corresponding object to cache NSCachedURLResponse *cachedResponse = proposedResponse; // If a processing method is passed through the block, it is called by passing the argumentif(self.dataTaskWillCacheResponse) { cachedResponse = self.dataTaskWillCacheResponse(session, dataTask, proposedResponse);  } // Callback resultif(completionHandler) { completionHandler(cachedResponse); }} /** When all messages in the session queue are sent, Will call the agent method * / - (void) URLSessionDidFinishEventsForBackgroundURLSession session: (NSURLSession *) {/ / master queue asynchronous callbackif(self.didFinishEventsForBackgroundURLSession) { dispatch_async(dispatch_get_main_queue(), ^{ self.didFinishEventsForBackgroundURLSession(session); }); }}Copy the code
  • NSURLSessionDownloadDelegate method implementation
/** When downloadTask completes a download, This proxy method is called */ - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask DidFinishDownloadingToURL location: (NSURL *) {/ / gain and downloadTask binding AFURLSessionManagerTaskDelegate * delegate delegate object = [self delegateForTask:downloadTask]; // If a block is passed in the processing modeif(self. DownloadTaskDidFinishDownloading) {/ / would pass and calls for download file path NSURL * fileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);if(fileURL) { delegate.downloadFileURL = fileURL; NSError *error = nil; // Move file, send notification if error, pass dataif(! [[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:&error]) { [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:error.userInfo]; }return; }} // Call the same delegate method implemented by the delegate object to process the resultif(delegate) { [delegate URLSession:session downloadTask:downloadTask didFinishDownloadingToURL:location]; }} /** downloadTask periodic callback agent */ - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten TotalBytesExpectedToWrite: int64_t totalBytesExpectedToWrite {/ / gain and downloadTask binding delegate object AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask]; // Call the same delegate method implemented by the delegate object to process dataif(delegate) { [delegate URLSession:session downloadTask:downloadTask didWriteData:bytesWritten totalBytesWritten:totalBytesWritten totalBytesExpectedToWrite:totalBytesExpectedToWrite]; } // Call block to call back dataif(self.downloadTaskDidWriteData) { self.downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite); }} /** When downloadTask resumes downloading, This proxy method is called */ - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask DidResumeAtOffset :(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes {// obtain the delegate object bound to the downloadTask AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask]; // Call the same delegate method implemented by the delegate object to process dataif(delegate) { [delegate URLSession:session downloadTask:downloadTask didResumeAtOffset:fileOffset expectedTotalBytes:expectedTotalBytes]; } // Call block to call back dataif(self.downloadTaskDidResume) { self.downloadTaskDidResume(session, downloadTask, fileOffset, expectedTotalBytes); }}Copy the code
  • NSSecureCoding method
+ (BOOL)supportsSecureCoding {
    return YES;
}

- (instancetype)initWithCoder:(NSCoder *)decoder {
    NSURLSessionConfiguration *configuration = [decoder decodeObjectOfClass:[NSURLSessionConfiguration class] forKey:@"sessionConfiguration"];

    self = [self initWithSessionConfiguration:configuration];
    if(! self) {return nil;
    }

    return self;
}

- (void)encodeWithCoder:(NSCoder *)coder {
    [coder encodeObject:self.session.configuration forKey:@"sessionConfiguration"];
}
Copy the code

About NSSecureCoding proxy method implementation in AFNetworking source reading (2), from AFURLRequestSerialization 4.1.2.4.7 in this article NSSecureCoding protocol methods have introduced the implementation of the paragraph.

  • NSCopying method implementation
- (instancetype)copyWithZone:(NSZone *)zone {
    return [[[self class] allocWithZone:zone] initWithSessionConfiguration:self.session.configuration];
}
Copy the code

3. Summary

So that’s it, AFURLSessionManager class. You can see that this class is responsible for managing the NSURLSession object, creating various types of tasks, listening to the state of its properties, and handling proxies and returned data.

AFNetworking

AFNetworking (A) — Start using it

Source: reading AFNetworking (2) — AFURLRequestSerialization

Source: reading AFNetworking (3) — AFURLResponseSerialization

AFNetworking (4) — AFSecurityPolicy

Source: reading AFNetworking (5) — AFNetworkReachabilityManager

AFNetworking (6) — Afurlssession Manager

AFNetworking (7) — Afhttpssession Manager

AFNetworking (8) – AFAutoPurgingImageCache

AFNetworking (9) — AFImageDownloader

The source code to read: AFNetworking (10) — AFNetworkActivityIndicatorManager

AFNetworking — UIActivityIndicatorView+AFNetworking

AFNetworking (12) — UIButton+AFNetworking

AFNetworking (13) — UIImageView+AFNetworking

UIProgressView+AFNetworking

AFNetworking — UIRefreshControl+AFNetworking

UIWebView+AFNetworking