AFNetworking 2.6.3 源碼解析

到了現(xiàn)在大部分項(xiàng)目使用的AFNetworking應(yīng)該是3.0以上版本,源碼分析基本上都是都是3.0的文章介紹,關(guān)于2.0的介紹只有bang神的博客.本文分析以AFNetworking 2.6.3為基礎(chǔ)進(jìn)行分析,如果大家已經(jīng)看過了類似文章,可以忽略我.

AFNetworking主要由NSURLConnction,NSURLSession,Security,Reachability和Serialization五個文件夾組成,AFNetworking對常見的UIKit組件進(jìn)行擴(kuò)展,這一部分就不介紹了,項(xiàng)目中實(shí)際使用的不多.

NSURLConnection

AFURLConnectionOperation是NSOperation的子類,實(shí)現(xiàn)的協(xié)議如下:
<pre><code>@interface AFURLConnectionOperation : NSOperation <NSURLConnectionDelegate, NSURLConnectionDataDelegate, NSSecureCoding, NSCopying></code></pre>

相對于日常開發(fā)中常用的NSCoding協(xié)議NSSecureCoding更加安全,可以防止信息被篡改,如果解檔的類和要求的類類型不一致的時候會拋出異常,詳情見參考鏈接.

單例請求的隊(duì)列queue和隊(duì)列組group,queue的命名堪稱典范:
<pre><code>`static dispatch_group_t url_request_operation_completion_group() {
static dispatch_group_t af_url_request_operation_completion_group;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
af_url_request_operation_completion_group = dispatch_group_create();
});

return af_url_request_operation_completion_group;

}

static dispatch_queue_t url_request_operation_completion_queue() {
static dispatch_queue_t af_url_request_operation_completion_queue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
af_url_request_operation_completion_queue = dispatch_queue_create("com.alamofire.networking.operation.queue", DISPATCH_QUEUE_CONCURRENT );
});

return af_url_request_operation_completion_queue;

}`</code></pre>

靜態(tài)內(nèi)聯(lián)函數(shù),static inline可以理解為static函數(shù)加入了inline屬性,編譯的時候會將其展開編譯而不是為函數(shù)生成獨(dú)立的匯編碼.
<pre><code>`static inline NSString * AFKeyPathFromOperationState(AFOperationState state) {
switch (state) {
case AFOperationReadyState:
return @"isReady";
case AFOperationExecutingState:
return @"isExecuting";
case AFOperationFinishedState:
return @"isFinished";
case AFOperationPausedState:
return @"isPaused";
default: {

pragma clang diagnostic push

pragma clang diagnostic ignored "-Wunreachable-code"

        return @"state";

pragma clang diagnostic pop

    }
}

}`</code></pre>

創(chuàng)建線程默認(rèn)是主線的Runloop下,Runloop模式默認(rèn)為NSDefaultRunLoopMode模式,如果用戶操作UI滑動屏幕,會切換到UITrackingRunLoopMode模式下,導(dǎo)致請求失效.AFNetworking創(chuàng)建一條常駐線程:
<pre><code>`+ (void)networkRequestThreadEntryPoint:(id)__unused object {
@autoreleasepool {
[[NSThread currentThread] setName:@"AFNetworking"];

    NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
    [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
    [runLoop run];
}

}

  • (NSThread *)networkRequestThread {
    static NSThread *_networkRequestThread = nil;
    static dispatch_once_t oncePredicate;
    dispatch_once(&oncePredicate, ^{
    _networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];
    [_networkRequestThread start];
    });

    return _networkRequestThread;
    }`</code></pre>

批量請求的代碼,線程的異步轉(zhuǎn)同步,線程之間的依賴寫的很??:
<pre><code>`+ (NSArray *)batchOfRequestOperations:(NSArray *)operations
progressBlock:(void (^)(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations))progressBlock
completionBlock:(void (^)(NSArray *operations))completionBlock
{
if (!operations || [operations count] == 0) {
return @[[NSBlockOperation blockOperationWithBlock:^{
dispatch_async(dispatch_get_main_queue(), ^{
if (completionBlock) {
completionBlock(@[]);
}
});
}]];
}

__block dispatch_group_t group = dispatch_group_create();
NSBlockOperation *batchedOperation = [NSBlockOperation blockOperationWithBlock:^{
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        if (completionBlock) {
            completionBlock(operations);
        }
    });
}];

for (AFURLConnectionOperation *operation in operations) {
    operation.completionGroup = group;
    void (^originalCompletionBlock)(void) = [operation.completionBlock copy];
    __weak __typeof(operation)weakOperation = operation;
    operation.completionBlock = ^{
        __strong __typeof(weakOperation)strongOperation = weakOperation;

pragma clang diagnostic push

pragma clang diagnostic ignored "-Wgnu"

        dispatch_queue_t queue = strongOperation.completionQueue ?: dispatch_get_main_queue();

pragma clang diagnostic pop

        dispatch_group_async(group, queue, ^{
            if (originalCompletionBlock) {
                originalCompletionBlock();
            }

            NSUInteger numberOfFinishedOperations = [[operations indexesOfObjectsPassingTest:^BOOL(id op, NSUInteger __unused idx,  BOOL __unused *stop) {
                return [op isFinished];
            }] count];

            if (progressBlock) {
                progressBlock(numberOfFinishedOperations, [operations count]);
            }

            dispatch_group_leave(group);
        });
    };

    dispatch_group_enter(group);
    [batchedOperation addDependency:operation];
}

return [operations arrayByAddingObject:batchedOperation];

}`</code></pre>

AFHTTPRequestOperation繼承自AFURLConnectionOperation,其中的暫停的操作對文件流的操作代碼如下:
<pre><code>`- (void)pause {
[super pause];

u_int64_t offset = 0;
if ([self.outputStream propertyForKey:NSStreamFileCurrentOffsetKey]) {
    offset = [(NSNumber *)[self.outputStream propertyForKey:NSStreamFileCurrentOffsetKey] unsignedLongLongValue];
} else {
    offset = [(NSData *)[self.outputStream propertyForKey:NSStreamDataWrittenToMemoryStreamKey] length];
}

NSMutableURLRequest *mutableURLRequest = [self.request mutableCopy];
if ([self.response respondsToSelector:@selector(allHeaderFields)] && [[self.response allHeaderFields] valueForKey:@"ETag"]) {
    [mutableURLRequest setValue:[[self.response allHeaderFields] valueForKey:@"ETag"] forHTTPHeaderField:@"If-Range"];
}
[mutableURLRequest setValue:[NSString stringWithFormat:@"bytes=%llu-", offset] forHTTPHeaderField:@"Range"];
self.request = mutableURLRequest;

}
`</code></pre>

AFHTTPRequestOperationManager對所有的Operation進(jìn)行管理,通過初始化就覆蓋了所有的核心類文件,序列化,安全協(xié)議和網(wǎng)絡(luò)請求管理文件.
<pre><code>`- (instancetype)initWithBaseURL:(NSURL *)url {
self = [super init];
if (!self) {
return nil;
}

// Ensure terminal slash for baseURL path, so that NSURL +URLWithString:relativeToURL: works as expected
if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) {
    url = [url URLByAppendingPathComponent:@""];
}

self.baseURL = url;

self.requestSerializer = [AFHTTPRequestSerializer serializer];
self.responseSerializer = [AFJSONResponseSerializer serializer];

self.securityPolicy = [AFSecurityPolicy defaultPolicy];

self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];

self.operationQueue = [[NSOperationQueue alloc] init];

self.shouldUseCredentialStorage = YES;

return self;

}
`</code></pre>

提供http請求的六種方式GET,HEAD,POST,PUT,PATCH,DELETE.
<pre><code>`- (nullable AFHTTPRequestOperation *)GET:(NSString *)URLString
parameters:(nullable id)parameters
success:(nullable void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(nullable void (^)(AFHTTPRequestOperation * __nullable operation, NSError *error))failure;

/**
Creates and runs an AFHTTPRequestOperation with a HEAD request.

@param URLString The URL string used to create the request URL.
@param parameters The parameters to be encoded according to the client request serializer.
@param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes a single arguments: the request operation.
@param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the request operation and the error describing the network or parsing error that occurred.

@see -HTTPRequestOperationWithRequest:success:failure:
*/

  • (nullable AFHTTPRequestOperation *)HEAD:(NSString *)URLString
    parameters:(nullable id)parameters
    success:(nullable void (^)(AFHTTPRequestOperation *operation))success
    failure:(nullable void (^)(AFHTTPRequestOperation * __nullable operation, NSError *error))failure;

/**
Creates and runs an AFHTTPRequestOperation with a POST request.

@param URLString The URL string used to create the request URL.
@param parameters The parameters to be encoded according to the client request serializer.
@param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes two arguments: the request operation, and the response object created by the client response serializer.
@param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the request operation and the error describing the network or parsing error that occurred.

@see -HTTPRequestOperationWithRequest:success:failure:
*/

  • (nullable AFHTTPRequestOperation *)POST:(NSString *)URLString
    parameters:(nullable id)parameters
    success:(nullable void (^)(AFHTTPRequestOperation *operation, id responseObject))success
    failure:(nullable void (^)(AFHTTPRequestOperation * __nullable operation, NSError *error))failure;

/**
Creates and runs an AFHTTPRequestOperation with a multipart POST request.

@param URLString The URL string used to create the request URL.
@param parameters The parameters to be encoded according to the client request serializer.
@param block A block that takes a single argument and appends data to the HTTP body. The block argument is an object adopting the AFMultipartFormData protocol.
@param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes two arguments: the request operation, and the response object created by the client response serializer.
@param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the request operation and the error describing the network or parsing error that occurred.

@see -HTTPRequestOperationWithRequest:success:failure:
*/

  • (nullable AFHTTPRequestOperation *)POST:(NSString *)URLString
    parameters:(nullable id)parameters
    constructingBodyWithBlock:(nullable void (^)(id <AFMultipartFormData> formData))block
    success:(nullable void (^)(AFHTTPRequestOperation *operation, id responseObject))success
    failure:(nullable void (^)(AFHTTPRequestOperation * __nullable operation, NSError *error))failure;

/**
Creates and runs an AFHTTPRequestOperation with a PUT request.

@param URLString The URL string used to create the request URL.
@param parameters The parameters to be encoded according to the client request serializer.
@param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes two arguments: the request operation, and the response object created by the client response serializer.
@param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the request operation and the error describing the network or parsing error that occurred.

@see -HTTPRequestOperationWithRequest:success:failure:
*/

  • (nullable AFHTTPRequestOperation *)PUT:(NSString *)URLString
    parameters:(nullable id)parameters
    success:(nullable void (^)(AFHTTPRequestOperation *operation, id responseObject))success
    failure:(nullable void (^)(AFHTTPRequestOperation * __nullable operation, NSError *error))failure;

/**
Creates and runs an AFHTTPRequestOperation with a PATCH request.

@param URLString The URL string used to create the request URL.
@param parameters The parameters to be encoded according to the client request serializer.
@param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes two arguments: the request operation, and the response object created by the client response serializer.
@param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the request operation and the error describing the network or parsing error that occurred.

@see -HTTPRequestOperationWithRequest:success:failure:
*/

  • (nullable AFHTTPRequestOperation *)PATCH:(NSString *)URLString
    parameters:(nullable id)parameters
    success:(nullable void (^)(AFHTTPRequestOperation *operation, id responseObject))success
    failure:(nullable void (^)(AFHTTPRequestOperation * __nullable operation, NSError *error))failure;

/**
Creates and runs an AFHTTPRequestOperation with a DELETE request.

@param URLString The URL string used to create the request URL.
@param parameters The parameters to be encoded according to the client request serializer.
@param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes two arguments: the request operation, and the response object created by the client response serializer.
@param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the request operation and the error describing the network or parsing error that occurred.

@see -HTTPRequestOperationWithRequest:success:failure:
*/

  • (nullable AFHTTPRequestOperation *)DELETE:(NSString *)URLString
    parameters:(nullable id)parameters
    success:(nullable void (^)(AFHTTPRequestOperation *operation, id responseObject))success
    failure:(nullable void (^)(AFHTTPRequestOperation * __nullable operation, NSError *error))failure;`</code></pre>

整個網(wǎng)絡(luò)請求回調(diào)順序:

AFNetworking.png

NSURLSession

NSURLSession文件夾下有兩個文件AFURLSessionManager和AFHTTPSessionManager.
<pre><code>@interface AFURLSessionManager : NSObject <NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate, NSSecureCoding, NSCopying></code></pre>

AFURLSessionManager有一個私有類_AFURLSessionTaskSwizzling,交換NSURLSessionTask中的暫停和繼續(xù)方法.

交換函數(shù):

<pre><code>`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);
}

static inline BOOL af_addMethod(Class theClass, SEL selector, Method method) {
return class_addMethod(theClass, selector, method_getImplementation(method), method_getTypeEncoding(method));
}
`</code></pre>

交換過程:

<pre><code>`

  • (void)load {
    /**
    WARNING: Trouble Ahead
    https://github.com/AFNetworking/AFNetworking/pull/2702
    */

    if (NSClassFromString(@"NSURLSessionTask")) {
    /**
    iOS 7 and iOS 8 differ in NSURLSessionTask implementation, which makes the next bit of code a bit tricky.
    Many Unit Tests have been built to validate as much of this behavior has possible.
    Here is what we know:
    - NSURLSessionTasks are implemented with class clusters, meaning the class you request from the API isn't actually the type of class you will get back.
    - Simply referencing [NSURLSessionTask class] will not work. You need to ask an NSURLSession to actually create an object, and grab the class from there.
    - On iOS 7, localDataTask is a __NSCFLocalDataTask, which inherits from __NSCFLocalSessionTask, which inherits from __NSCFURLSessionTask.
    - On iOS 8, localDataTask is a __NSCFLocalDataTask, which inherits from __NSCFLocalSessionTask, which inherits from NSURLSessionTask.
    - On iOS 7, __NSCFLocalSessionTask and __NSCFURLSessionTask are the only two classes that have their own implementations of resume and suspend, and __NSCFLocalSessionTask DOES NOT CALL SUPER. This means both classes need to be swizzled.
    - On iOS 8, NSURLSessionTask is the only class that implements resume and suspend. This means this is the only class that needs to be swizzled.
    - Because NSURLSessionTask is not involved in the class hierarchy for every version of iOS, its easier to add the swizzled methods to a dummy class and manage them there.

       Some Assumptions:
          - No implementations of `resume` or `suspend` call super. If this were to change in a future version of iOS, we'd need to handle it.
          - No background task classes override `resume` or `suspend`
       
       The current solution:
          1) Grab an instance of `__NSCFLocalDataTask` by asking an instance of `NSURLSession` for a data task.
          2) Grab a pointer to the original implementation of `af_resume`
          3) Check to see if the current class has an implementation of resume. If so, continue to step 4.
          4) Grab the super class of the current class.
          5) Grab a pointer for the current class to the current implementation of `resume`.
          6) Grab a pointer for the super class to the current implementation of `resume`.
          7) If the current class implementation of `resume` is not equal to the super class implementation of `resume` AND the current implementation of `resume` is not equal to the original implementation of `af_resume`, THEN swizzle the methods
          8) Set the current class to the super class, and repeat steps 3-8
       */
      NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
      NSURLSession * session = [NSURLSession sessionWithConfiguration:configuration];
    

pragma GCC diagnostic push

pragma GCC diagnostic ignored "-Wnonnull"

    NSURLSessionDataTask *localDataTask = [session dataTaskWithURL:nil];

pragma clang diagnostic pop

    IMP originalAFResumeIMP = method_getImplementation(class_getInstanceMethod([self class], @selector(af_resume)));
    Class currentClass = [localDataTask class];
    
    while (class_getInstanceMethod(currentClass, @selector(resume))) {
        Class superClass = [currentClass superclass];
        IMP classResumeIMP = method_getImplementation(class_getInstanceMethod(currentClass, @selector(resume)));
        IMP superclassResumeIMP = method_getImplementation(class_getInstanceMethod(superClass, @selector(resume)));
        if (classResumeIMP != superclassResumeIMP &&
            originalAFResumeIMP != classResumeIMP) {
            [self swizzleResumeAndSuspendMethodForClass:currentClass];
        }
        currentClass = [currentClass superclass];
    }
    
    [localDataTask cancel];
    [session finishTasksAndInvalidate];
}

}

  • (void)swizzleResumeAndSuspendMethodForClass:(Class)theClass {
    Method afResumeMethod = class_getInstanceMethod(self, @selector(af_resume));
    Method afSuspendMethod = class_getInstanceMethod(self, @selector(af_suspend));

    if (af_addMethod(theClass, @selector(af_resume), afResumeMethod)) {
    af_swizzleSelector(theClass, @selector(resume), @selector(af_resume));
    }

    if (af_addMethod(theClass, @selector(af_suspend), afSuspendMethod)) {
    af_swizzleSelector(theClass, @selector(suspend), @selector(af_suspend));
    }
    }`</code></pre>

初始化過程與AFURLConnectionOperation的常駐線程處理方式不一樣,設(shè)置最大并發(fā)數(shù)量為1,想當(dāng)于是串行隊(duì)列.
<pre><code>`- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
self = [super init];
if (!self) {
return nil;
}

if (!configuration) {
    configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
}

self.sessionConfiguration = configuration;

self.operationQueue = [[NSOperationQueue alloc] init];
self.operationQueue.maxConcurrentOperationCount = 1;

self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];

self.responseSerializer = [AFJSONResponseSerializer serializer];

self.securityPolicy = [AFSecurityPolicy defaultPolicy];

if !TARGET_OS_WATCH

self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];

endif

self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];

self.lock = [[NSLock alloc] init];
self.lock.name = AFURLSessionManagerLockName;

[self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
    for (NSURLSessionDataTask *task in dataTasks) {
        [self addDelegateForDataTask:task completionHandler:nil];
    }

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

    for (NSURLSessionDownloadTask *downloadTask in downloadTasks) {
        [self addDelegateForDownloadTask:downloadTask progress:nil destination:nil completionHandler:nil];
    }
}];

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidResume:) name:AFNSURLSessionTaskDidResumeNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidSuspend:) name:AFNSURLSessionTaskDidSuspendNotification object:nil];

return self;

}`</code></pre>

任務(wù)的暫停和繼續(xù)通過通知來執(zhí)行:
<pre><code>`- (void)taskDidResume:(NSNotification *)notification {
NSURLSessionTask *task = notification.object;
if ([task respondsToSelector:@selector(taskDescription)]) {
if ([task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks]) {
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidResumeNotification object:task];
});
}
}
}

  • (void)taskDidSuspend:(NSNotification *)notification {
    NSURLSessionTask *task = notification.object;
    if ([task respondsToSelector:@selector(taskDescription)]) {
    if ([task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks]) {
    dispatch_async(dispatch_get_main_queue(), ^{
    [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidSuspendNotification object:task];
    });
    }
    }
    }`</code></pre>

進(jìn)度管理通過AFURLSessionManagerTaskDelegate來實(shí)現(xiàn):
<pre><code>@interface AFURLSessionManagerTaskDelegate : NSObject <NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate> @property (nonatomic, weak) AFURLSessionManager *manager; @property (nonatomic, strong) NSMutableData *mutableData; @property (nonatomic, strong) NSProgress *progress; @property (nonatomic, copy) NSURL *downloadFileURL; @property (nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading; @property (nonatomic, copy) AFURLSessionTaskCompletionHandler completionHandler; @end</code></pre>

異步轉(zhuǎn)同步的另外一種實(shí)現(xiàn)方式:
<pre><code>`- (NSArray *)tasksForKeyPath:(NSString *)keyPath {
__block NSArray *tasks = nil;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
if ([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(@selector(tasks))]) {
tasks = [@[dataTasks, uploadTasks, downloadTasks] valueForKeyPath:@"@unionOfArrays.self"];
}

    dispatch_semaphore_signal(semaphore);
}];

dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

return tasks;

}

  • (NSArray *)tasks {
    return [self tasksForKeyPath:NSStringFromSelector(_cmd)];
    }
    `</code></pre>

AFHTTPSessionManger是AFURLSessionManager的子類,提供了GET,HEAD,POST,PUT,PATCH和DELETE六種訪問方式.
<pre><code>`- (nullable NSURLSessionDataTask *)GET:(NSString *)URLString
parameters:(nullable id)parameters
success:(nullable void (^)(NSURLSessionDataTask *task, id responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * __nullable task, NSError *error))failure;

/**
Creates and runs an NSURLSessionDataTask with a HEAD request.

@param URLString The URL string used to create the request URL.
@param parameters The parameters to be encoded according to the client request serializer.
@param success A block object to be executed when the task finishes successfully. This block has no return value and takes a single arguments: the data task.
@param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.

@see -dataTaskWithRequest:completionHandler:
*/

  • (nullable NSURLSessionDataTask *)HEAD:(NSString *)URLString
    parameters:(nullable id)parameters
    success:(nullable void (^)(NSURLSessionDataTask *task))success
    failure:(nullable void (^)(NSURLSessionDataTask * __nullable task, NSError *error))failure;

/**
Creates and runs an NSURLSessionDataTask with a POST request.

@param URLString The URL string used to create the request URL.
@param parameters The parameters to be encoded according to the client request serializer.
@param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
@param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.

@see -dataTaskWithRequest:completionHandler:
*/

  • (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
    parameters:(nullable id)parameters
    success:(nullable void (^)(NSURLSessionDataTask *task, id responseObject))success
    failure:(nullable void (^)(NSURLSessionDataTask * __nullable task, NSError *error))failure;

/**
Creates and runs an NSURLSessionDataTask with a multipart POST request.

@param URLString The URL string used to create the request URL.
@param parameters The parameters to be encoded according to the client request serializer.
@param block A block that takes a single argument and appends data to the HTTP body. The block argument is an object adopting the AFMultipartFormData protocol.
@param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
@param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.

@see -dataTaskWithRequest:completionHandler:
*/

  • (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
    parameters:(nullable id)parameters
    constructingBodyWithBlock:(nullable void (^)(id <AFMultipartFormData> formData))block
    success:(nullable void (^)(NSURLSessionDataTask *task, id responseObject))success
    failure:(nullable void (^)(NSURLSessionDataTask * __nullable task, NSError *error))failure;

/**
Creates and runs an NSURLSessionDataTask with a PUT request.

@param URLString The URL string used to create the request URL.
@param parameters The parameters to be encoded according to the client request serializer.
@param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
@param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.

@see -dataTaskWithRequest:completionHandler:
*/

  • (nullable NSURLSessionDataTask *)PUT:(NSString *)URLString
    parameters:(nullable id)parameters
    success:(nullable void (^)(NSURLSessionDataTask *task, id responseObject))success
    failure:(nullable void (^)(NSURLSessionDataTask * __nullable task, NSError *error))failure;

/**
Creates and runs an NSURLSessionDataTask with a PATCH request.

@param URLString The URL string used to create the request URL.
@param parameters The parameters to be encoded according to the client request serializer.
@param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
@param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.

@see -dataTaskWithRequest:completionHandler:
*/

  • (nullable NSURLSessionDataTask *)PATCH:(NSString *)URLString
    parameters:(nullable id)parameters
    success:(nullable void (^)(NSURLSessionDataTask *task, id responseObject))success
    failure:(nullable void (^)(NSURLSessionDataTask * __nullable task, NSError *error))failure;

/**
Creates and runs an NSURLSessionDataTask with a DELETE request.

@param URLString The URL string used to create the request URL.
@param parameters The parameters to be encoded according to the client request serializer.
@param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
@param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.

@see -dataTaskWithRequest:completionHandler:
*/

  • (nullable NSURLSessionDataTask *)DELETE:(NSString *)URLString
    parameters:(nullable id)parameters
    success:(nullable void (^)(NSURLSessionDataTask *task, id responseObject))success
    failure:(nullable void (^)(NSURLSessionDataTask * __nullable task, NSError *error))failure;
    `</code></pre>

Security

AFSecurityPolicy主要用于驗(yàn)證HTTPS請求的證書,驗(yàn)證的方式有三種.

<pre><code>typedef NS_ENUM(NSUInteger, AFSSLPinningMode) { AFSSLPinningModeNone, AFSSLPinningModePublicKey, AFSSLPinningModeCertificate, };</code></pre>

AFSSLPinningModeNone:直接在系統(tǒng)信任的證書列表中查找,只有系統(tǒng)信任機(jī)構(gòu)簽發(fā)的證書才能通過.

AFSSLPinningModePublicKey:客戶端保存服務(wù)端證書的拷貝,檢驗(yàn)證書的公鑰和服務(wù)端返回證書的公鑰是否一致;

AFSSLPinningModeCertificate:客戶端保存服務(wù)端證書的拷貝,先驗(yàn)證證書的域名有效期等信息,然后對比客戶端的證書和服務(wù)端的正式是否一致;

安全校驗(yàn)代碼:
<pre><code>`

  • (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust {
    return [self evaluateServerTrust:serverTrust forDomain:nil];
    }

  • (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
    forDomain:(NSString *)domain
    {
    if (domain && self.allowInvalidCertificates && self.validatesDomainName && (self.SSLPinningMode == AFSSLPinningModeNone || [self.pinnedCertificates count] == 0)) {
    // https://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/NetworkingTopics/Articles/OverridingSSLChainValidationCorrectly.html
    // According to the docs, you should only trust your provided certs for evaluation.
    // Pinned certificates are added to the trust. Without pinned certificates,
    // there is nothing to evaluate against.
    //
    // From Apple Docs:
    // "Do not implicitly trust self-signed certificates as anchors (kSecTrustOptionImplicitAnchors).
    // Instead, add your own (self-signed) CA certificate to the list of trusted anchors."
    NSLog(@"In order to validate a domain name for self signed certificates, you MUST use pinning.");
    return NO;
    }

    NSMutableArray *policies = [NSMutableArray array];
    if (self.validatesDomainName) {
    [policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];
    } else {
    [policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];
    }

    SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);

    if (self.SSLPinningMode == AFSSLPinningModeNone) {
    return self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust);
    } else if (!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) {
    return NO;
    }

    NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust);
    switch (self.SSLPinningMode) {
    case AFSSLPinningModeNone:
    default:
    return NO;
    case AFSSLPinningModeCertificate: {
    NSMutableArray *pinnedCertificates = [NSMutableArray array];
    for (NSData *certificateData in self.pinnedCertificates) {
    [pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)];
    }
    SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates);

          if (!AFServerTrustIsValid(serverTrust)) {
              return NO;
          }
    
          NSUInteger trustedCertificateCount = 0;
          for (NSData *trustChainCertificate in serverCertificates) {
              if ([self.pinnedCertificates containsObject:trustChainCertificate]) {
                  trustedCertificateCount++;
              }
          }
          return trustedCertificateCount > 0;
      }
      case AFSSLPinningModePublicKey: {
          NSUInteger trustedPublicKeyCount = 0;
          NSArray *publicKeys = AFPublicKeyTrustChainForServerTrust(serverTrust);
    
          for (id trustChainPublicKey in publicKeys) {
              for (id pinnedPublicKey in self.pinnedPublicKeys) {
                  if (AFSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) {
                      trustedPublicKeyCount += 1;
                  }
              }
          }
          return trustedPublicKeyCount > 0;
      }
    

    }

    return NO;
    }`</code></pre>

最終會通過C語言函數(shù)進(jìn)行信任校驗(yàn):
<pre><code>`static NSArray * AFCertificateTrustChainForServerTrust(SecTrustRef serverTrust) {
CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust);
NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount];

for (CFIndex i = 0; i < certificateCount; i++) {
    SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i);
    [trustChain addObject:(__bridge_transfer NSData *)SecCertificateCopyData(certificate)];
}

return [NSArray arrayWithArray:trustChain];

}

static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) {
SecPolicyRef policy = SecPolicyCreateBasicX509();
CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust);
NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount];
for (CFIndex i = 0; i < certificateCount; i++) {
SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i);

    SecCertificateRef someCertificates[] = {certificate};
    CFArrayRef certificates = CFArrayCreate(NULL, (const void **)someCertificates, 1, NULL);

    SecTrustRef trust;
    __Require_noErr_Quiet(SecTrustCreateWithCertificates(certificates, policy, &trust), _out);

    SecTrustResultType result;
    __Require_noErr_Quiet(SecTrustEvaluate(trust, &result), _out);

    [trustChain addObject:(__bridge_transfer id)SecTrustCopyPublicKey(trust)];

_out:
    if (trust) {
        CFRelease(trust);
    }

    if (certificates) {
        CFRelease(certificates);
    }

    continue;
}
CFRelease(policy);

return [NSArray arrayWithArray:trustChain];

}`</code></pre>

Reachability

監(jiān)聽當(dāng)前App的網(wǎng)絡(luò)請求狀態(tài),有網(wǎng),無網(wǎng),4G還是wifi.
<pre><code>typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) { AFNetworkReachabilityStatusUnknown = -1, AFNetworkReachabilityStatusNotReachable = 0, AFNetworkReachabilityStatusReachableViaWWAN = 1, AFNetworkReachabilityStatusReachableViaWiFi = 2, };</code></pre>

經(jīng)典的單例模式又出現(xiàn)了:
<pre><code>`+ (instancetype)sharedManager {
static AFNetworkReachabilityManager *_sharedManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
struct sockaddr_in address;
bzero(&address, sizeof(address));
address.sin_len = sizeof(address);
address.sin_family = AF_INET;

    _sharedManager = [self managerForAddress:&address];
});

return _sharedManager;

}`</code></pre>

代碼中的sockaddr_in用來處理網(wǎng)絡(luò)通信的地址:
<pre><code>struct sockaddr_in { __uint8_t sin_len; sa_family_t sin_family; in_port_t sin_port; struct in_addr sin_addr; char sin_zero[8]; };</code></pre>

sin_family表示協(xié)議族,sin_port表示端口,sin_addr表示網(wǎng)絡(luò)地址,sin_zero填充字節(jié).

sockaddr_in的比較對象是sockaddr,sockaddr是給操作系統(tǒng)用的,sockaddr的sa_data字段被sockaddr_in擴(kuò)展成了三個字段.
<pre><code>struct sockaddr { __uint8_t sa_len; /* total length */ sa_family_t sa_family; /* [XSI] address family */ char sa_data[14]; /* [XSI] addr value (actually larger) */ };</code></pre>

通過SCNetworkReachabilityContext來監(jiān)控網(wǎng)絡(luò)變化:
<pre><code>`- (void)startMonitoring {
[self stopMonitoring];

if (!self.networkReachability) {
    return;
}

__weak __typeof(self)weakSelf = self;
AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {
    __strong __typeof(weakSelf)strongSelf = weakSelf;

    strongSelf.networkReachabilityStatus = status;
    if (strongSelf.networkReachabilityStatusBlock) {
        strongSelf.networkReachabilityStatusBlock(status);
    }

};

id networkReachability = self.networkReachability;
SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL};
SCNetworkReachabilitySetCallback((__bridge SCNetworkReachabilityRef)networkReachability, AFNetworkReachabilityCallback, &context);
SCNetworkReachabilityScheduleWithRunLoop((__bridge SCNetworkReachabilityRef)networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{
    SCNetworkReachabilityFlags flags;
    if (SCNetworkReachabilityGetFlags((__bridge SCNetworkReachabilityRef)networkReachability, &flags)) {
        AFPostReachabilityStatusChange(flags, callback);
    }
});

}

  • (void)stopMonitoring {
    if (!self.networkReachability) {
    return;
    }

    SCNetworkReachabilityUnscheduleFromRunLoop((__bridge SCNetworkReachabilityRef)self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
    }`</code></pre>

Serialization

序列化數(shù)據(jù)有兩個主要的類AFURLRequestSerialization和AFURLResponseSerialization.

查詢字符串百分比編碼,經(jīng)典如下:
<pre><code>`static NSString * AFPercentEscapedStringFromString(NSString string) {
static NSString * const kAFCharactersGeneralDelimitersToEncode = @":#[]@"; // does not include "?" or "/" due to RFC 3986 - Section 3.4
static NSString * const kAFCharactersSubDelimitersToEncode = @"!$&'()
+,;=";

NSMutableCharacterSet * allowedCharacterSet = [[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy];
[allowedCharacterSet removeCharactersInString:[kAFCharactersGeneralDelimitersToEncode stringByAppendingString:kAFCharactersSubDelimitersToEncode]];

// FIXME: https://github.com/AFNetworking/AFNetworking/pull/3028
// return [string stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet];

static NSUInteger const batchSize = 50;

NSUInteger index = 0;
NSMutableString *escaped = @"".mutableCopy;

while (index < string.length) {

pragma GCC diagnostic push

pragma GCC diagnostic ignored "-Wgnu"

    NSUInteger length = MIN(string.length - index, batchSize);

pragma GCC diagnostic pop

    NSRange range = NSMakeRange(index, length);

    // To avoid breaking up character sequences such as ????????
    range = [string rangeOfComposedCharacterSequencesForRange:range];

    NSString *substring = [string substringWithRange:range];
    NSString *encoded = [substring stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet];
    [escaped appendString:encoded];

    index += range.length;
}

return escaped;

}
`</code></pre>

AFNetworking文件上傳的封裝的非常巧妙:
<pre><code>`- (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(NSDictionary *)parameters
constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
{
return [self multipartFormRequestWithMethod:method URLString:URLString parameters:parameters constructingBodyWithBlock:block error:nil];
}

  • (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method
    URLString:(NSString *)URLString
    parameters:(NSDictionary *)parameters
    constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
    error:(NSError *__autoreleasing *)error
    {
    NSParameterAssert(method);
    NSParameterAssert(![method isEqualToString:@"GET"] && ![method isEqualToString:@"HEAD"]);

    NSMutableURLRequest *mutableRequest = [self requestWithMethod:method URLString:URLString parameters:nil error:error];

    __block AFStreamingMultipartFormData *formData = [[AFStreamingMultipartFormData alloc] initWithURLRequest:mutableRequest stringEncoding:NSUTF8StringEncoding];

    if (parameters) {
    for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
    NSData *data = nil;
    if ([pair.value isKindOfClass:[NSData class]]) {
    data = pair.value;
    } else if ([pair.value isEqual:[NSNull null]]) {
    data = [NSData data];
    } else {
    data = [[pair.value description] dataUsingEncoding:self.stringEncoding];
    }

          if (data) {
              [formData appendPartWithFormData:data name:[pair.field description]];
          }
      }
    

    }

    if (block) {
    block(formData);
    }

    return [formData requestByFinalizingMultipartFormData];
    }`</code></pre>

文件上傳請求的流程圖如下:

AFURLRequestSerialization.png

AFHTTPBodyPart封裝了各部分?jǐn)?shù)據(jù)的組裝和讀取,一個AFHTTPBodyPart就是一個數(shù)據(jù)塊.FileURL/NSData/NSInputStream的數(shù)據(jù)在AFHTTPBodyPart都轉(zhuǎn)成NSInputStream,讀取數(shù)據(jù)時只需讀這個inputStream.inputStream只保存了數(shù)據(jù)的實(shí)體,沒有包括分隔符和頭部,AFHTTPBodyPart邊讀取邊上傳,每次讀取的大小有限制,通過狀態(tài)機(jī)判斷數(shù)據(jù)讀取進(jìn)度,讀取進(jìn)度方法如下:

<pre><code>`- (NSInteger)read:(uint8_t *)buffer
maxLength:(NSUInteger)length
{
if ([self streamStatus] == NSStreamStatusClosed) {
return 0;
}

NSInteger totalNumberOfBytesRead = 0;

pragma clang diagnostic push

pragma clang diagnostic ignored "-Wgnu"

while ((NSUInteger)totalNumberOfBytesRead < MIN(length, self.numberOfBytesInPacket)) {
    if (!self.currentHTTPBodyPart || ![self.currentHTTPBodyPart hasBytesAvailable]) {
        if (!(self.currentHTTPBodyPart = [self.HTTPBodyPartEnumerator nextObject])) {
            break;
        }
    } else {
        NSUInteger maxLength = length - (NSUInteger)totalNumberOfBytesRead;
        NSInteger numberOfBytesRead = [self.currentHTTPBodyPart read:&buffer[totalNumberOfBytesRead] maxLength:maxLength];
        if (numberOfBytesRead == -1) {
            self.streamError = self.currentHTTPBodyPart.inputStream.streamError;
            break;
        } else {
            totalNumberOfBytesRead += numberOfBytesRead;

            if (self.delay > 0.0f) {
                [NSThread sleepForTimeInterval:self.delay];
            }
        }
    }
}

pragma clang diagnostic pop

return totalNumberOfBytesRead;

}`</code></pre>

AFURLResponseSerialization響應(yīng)序列化:
<pre><code>@protocol AFURLResponseSerialization <NSObject, NSSecureCoding, NSCopying></code></pre>

初始化設(shè)置了可接受的成功狀態(tài)碼:
<pre><code>`- (instancetype)init {
self = [super init];
if (!self) {
return nil;
}

self.stringEncoding = NSUTF8StringEncoding;

self.acceptableStatusCodes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 100)];
self.acceptableContentTypes = nil;

return self;

}`</code></pre>

AFImageResponseSerializer:接收的圖片序列化主流圖片格式都有判斷.
<pre><code>`@implementation AFImageResponseSerializer

  • (instancetype)init {
    self = [super init];
    if (!self) {
    return nil;
    }

    self.acceptableContentTypes = [[NSSet alloc] initWithObjects:@"image/tiff", @"image/jpeg", @"image/gif", @"image/png", @"image/ico", @"image/x-icon", @"image/bmp", @"image/x-bmp", @"image/x-xbitmap", @"image/x-win-bitmap", nil];

if TARGET_OS_IOS

self.imageScale = [[UIScreen mainScreen] scale];
self.automaticallyInflatesResponseImage = YES;

elif TARGET_OS_WATCH

self.imageScale = [[WKInterfaceDevice currentDevice] screenScale];
self.automaticallyInflatesResponseImage = YES;

endif

return self;

}`</code></pre>

刪除返回對象中的null值的輔助函數(shù):
<pre><code>`static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingOptions readingOptions) {
if ([JSONObject isKindOfClass:[NSArray class]]) {
NSMutableArray *mutableArray = [NSMutableArray arrayWithCapacity:[(NSArray *)JSONObject count]];
for (id value in (NSArray *)JSONObject) {
[mutableArray addObject:AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions)];
}

    return (readingOptions & NSJSONReadingMutableContainers) ? mutableArray : [NSArray arrayWithArray:mutableArray];
} else if ([JSONObject isKindOfClass:[NSDictionary class]]) {
    NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionaryWithDictionary:JSONObject];
    for (id <NSCopying> key in [(NSDictionary *)JSONObject allKeys]) {
        id value = (NSDictionary *)JSONObject[key];
        if (!value || [value isEqual:[NSNull null]]) {
            [mutableDictionary removeObjectForKey:key];
        } else if ([value isKindOfClass:[NSArray class]] || [value isKindOfClass:[NSDictionary class]]) {
            mutableDictionary[key] = AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions);
        }
    }

    return (readingOptions & NSJSONReadingMutableContainers) ? mutableDictionary : [NSDictionary dictionaryWithDictionary:mutableDictionary];
}

return JSONObject;

}`</code></pre>

特別感謝bang神的博客三年前的博客,現(xiàn)在讀起來依然是干貨十足.文中關(guān)鍵代碼并沒有中文注釋,代碼塊都不長,正常讀起來應(yīng)該都沒問題,如有問題歡迎評論區(qū)探討.

AFNetWorking是如何避免循環(huán)引用的?
項(xiàng)目開中為了避免控制器或者視圖不釋放,經(jīng)常會檢查Block在調(diào)用的過程是否會發(fā)生循環(huán)引用,當(dāng)發(fā)起網(wǎng)絡(luò)請求的的時候,self持有opration,operation如果直接持有self,肯定會出現(xiàn)循環(huán)引用,但是開始中我們可以直接寫self.

AFNetWorking在block后調(diào)用[strongSelf setCompletionBlock:nil]把completionBlock設(shè)成nil,手動釋放self(NSOperation對象)持有的completionBlock對象,打破循環(huán)引用.
<pre><code>`- (void)setCompletionBlock:(void (^)(void))block {
[self.lock lock];
if (!block) {
[super setCompletionBlock:nil];
} else {
__weak __typeof(self)weakSelf = self;
[super setCompletionBlock:^ {
__strong __typeof(weakSelf)strongSelf = weakSelf;

pragma clang diagnostic push

pragma clang diagnostic ignored "-Wgnu"

        dispatch_group_t group = strongSelf.completionGroup ?: url_request_operation_completion_group();
        dispatch_queue_t queue = strongSelf.completionQueue ?: dispatch_get_main_queue();

pragma clang diagnostic pop

        dispatch_group_async(group, queue, ^{
            block();
        });

        dispatch_group_notify(group, url_request_operation_completion_queue(), ^{
            [strongSelf setCompletionBlock:nil];
        });
    }];
}
[self.lock unlock];

}`</code></pre>

參考資料:
AFNetworking2.0源碼解析<一>
使用NSSecureCoding協(xié)議進(jìn)行對象編解碼
https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Streams/Articles/CocoaStreamsOverview.html#//apple_ref/doc/uid/20002272-BABJFBBB
sockaddr和sockaddr_in的區(qū)別

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,321評論 6 543
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,559評論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,442評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,835評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,581評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,922評論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,931評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,096評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,639評論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,374評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,591評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,104評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,789評論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,196評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,524評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,322評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,554評論 2 379

推薦閱讀更多精彩內(nèi)容