AFNetworking源碼閱讀1

#前言:AFNetworking是一款很是有名網絡框架,筆者也是用了挺久了,可是也是僅限於使用一些方法。最近一直在思考,要想成爲一名優秀的開發者,確定不能僅僅停留在會用的階段。因此痛定思痛,此次下定決心,本身的第一遍博客就從AFN開始。json

從上圖AFN的目錄咱們能夠看出,ANF大概分爲了5個類:

  1. NSURLSession(通訊模塊,對NSURLSession的封裝,也是AFN最核心的地方)
  2. Reachability(網絡狀態監聽模塊)
  3. Security(網絡安全模塊)
  4. Serialization(數據解析模塊)
  5. UIKIT(UI模塊)

下面我會按着模塊來分析:設計模式

#通訊模塊xcode

##先從NSURLSession開始分析:安全

@interface AFHTTPSessionManager : AFURLSessionManager <NSSecureCoding, NSCopying>
複製代碼

打開AFHTTPSessionManager,首先就能明白AFHTTPSessionManager繼承自AFURLSessionManager,屬於這個通訊模塊的最上層。bash

+ (instancetype)manager {
    return [[[self class] alloc] initWithBaseURL:nil];
}

- (instancetype)init {
    return [self initWithBaseURL:nil];
}

- (instancetype)initWithBaseURL:(NSURL *)url {
    return [self initWithBaseURL:url sessionConfiguration:nil];
}

- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
    return [self initWithBaseURL:nil sessionConfiguration:configuration];
}

複製代碼

而在其.m中看看到初始化,[AFHTTPSessionManager manager]也就是咱們平常調用AFN開始網絡通訊的初始化。其用的設計模式就是工廠模式。網絡

*/

- (instancetype)initWithBaseURL:(NSURL *)url
           sessionConfiguration:(NSURLSessionConfiguration *)configuration
{
    //調用父類初始化方法
    self = [super initWithSessionConfiguration:configuration];
    if (!self) {
        return nil;
    }

    /*
     爲了確保NSURL +URLWithString:relativeToURL: works能夠正確執行,在baseurlpath的最後添加‘/’
     */
    //url有值且沒有‘/’,那麼在url的末尾添加‘/’
    if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) {
        url = [url URLByAppendingPathComponent:@""];
    }
    self.baseURL = url;
    //給requestSerializer、responseSerializer設置默認值
    self.requestSerializer = [AFHTTPRequestSerializer serializer];
    self.responseSerializer = [AFJSONResponseSerializer serializer];

    return self;
}
複製代碼

其中self = [super initWithSessionConfiguration:configuration];會去調用父類(AFURLSessionManager)的initWithSessionConfiguration的初始化方法,進行一些url初始化的配置session

- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
    self = [super init];
    if (!self) {
        return nil;
    }

    //設置默認的configuration,配置咱們的session
    if (!configuration) {
        configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    }

    //持有configuration
    self.sessionConfiguration = configuration;

    //設置爲delegate的操做隊列併發的線程數量1,也就是串行隊列
    //避免了線程的不安全,
    self.operationQueue = [[NSOperationQueue alloc] init];
    self.operationQueue.maxConcurrentOperationCount = 1;

    /*
     -若是完成後須要作複雜(耗時)的處理,能夠選擇異步隊列
     -若是完成後直接更新UI,能夠選擇主隊列
     [NSOperationQueue mainQueue]
     */
    
    self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];

    //默認爲json解析
    self.responseSerializer = [AFJSONResponseSerializer serializer];

    //設置默認證書 無條件信任證書https認證
    self.securityPolicy = [AFSecurityPolicy defaultPolicy];

#if !TARGET_OS_WATCH
    //網絡狀態監聽
    self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
#endif

    //delegate= value taskid = key
    self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];

    //使用NSLock確保線程安全
    self.lock = [[NSLock alloc] init];
    self.lock.name = AFURLSessionManagerLockName;

    
    //異步的獲取當前session的全部未完成的task。
    //後臺任務從新回來初始化session,可能就會有先前的任務
    //保證剩餘的操做不會被浪費掉
    [self.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 in downloadTasks) {
            [self addDelegateForDownloadTask:downloadTask progress:nil destination:nil completionHandler:nil];
        }
    }];

    return self;
}
複製代碼

以上就是AFN在初始化的網絡模塊運行的方法 其核心全局的session也是在URLSessionManager中建立出來併發

AFHTTPSessionManager做爲一個供開發者調用的模塊,其.h暴露出來的API都是簡單明瞭,只須要提供必要的URL和params就能夠經過block返回結果。框架

下面咱們先簡單看下ANF在請求的過程當中的流程異步

- (NSURLSessionDataTask *)GET:(NSString *)URLString
                   parameters:(id)parameters
                     progress:(void (^)(NSProgress * _Nonnull))downloadProgress
                      success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
                      failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
{

    //返回一個task,而後開始網絡請求
    NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
                                                        URLString:URLString
                                                       parameters:parameters
                                                   uploadProgress:nil
                                                 downloadProgress:downloadProgress
                                                          success:success
                                                          failure:failure];

    //開始網絡請求
    [dataTask resume];

    return dataTask;
}

- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *) method
                                       URLString:(NSString *)URLString
                                      parameters:(id)parameters
                                  uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
                                downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress
                                         success:(void (^)(NSURLSessionDataTask *, id))success
                                         failure:(void (^)(NSURLSessionDataTask *, NSError *))failure
{
    NSError *serializationError = nil;
    /*
     1.先調用AFHTTPRequestSerializer的requestWithMethod函數構建request
     2.處理request構建產生的錯誤 – serializationError
     //relativeToURL表示將URLString拼接到baseURL後面

     */
    NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
    if (serializationError) {
        if (failure) {
            //http://fuckingclangwarnings.com/#semantic
            //xcode忽略編譯器的警告,diagnostic:診斷的
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
            //completionQueue不存在返回dispatch_get_main_queue
            dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
                failure(nil, serializationError);
            });
#pragma clang diagnostic pop
        }

        return nil;
    }

    //此時的request已經將參數拼接在url後面
    __block NSURLSessionDataTask *dataTask = nil;
    dataTask = [self dataTaskWithRequest:request
                          uploadProgress:uploadProgress
                        downloadProgress:downloadProgress
                       completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
        if (error) {
            if (failure) {
                failure(dataTask, error);
            }
        } else {
            if (success) {
                success(dataTask, responseObject);
            }
        }
    }];

    return dataTask;
}



複製代碼

不論是調用GET/POST(除了表單,可是表單是調用一個返回NSURLSessionDataTask的方法)/DELETE/PATCH/PUT,都會調用dataTaskWithHTTPMethod

NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];

複製代碼

在上面的方法中,生成了一個request對象

//使用session和request來建立一個NSURLSessionDataTask對象
 dataTask = [self.session dataTaskWithRequest:request];

 [self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
複製代碼

經過session和request來生成一個task

AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
    delegate.manager = self;
    delegate.completionHandler = completionHandler;
    [self setDelegate:delegate forTask:dataTask];
複製代碼

而task和delegate綁定在一塊兒

- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
            forTask:(NSURLSessionTask *)task
{
    //task和delegate都不能爲空
    NSParameterAssert(task);
    NSParameterAssert(delegate);

    //加鎖確保中間代碼塊是原子操做,線程安全
    [self.lock lock];
    //將delegate存入字典,以taskid做爲key,說明每一個task都有各自的代理
    self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
    //設置兩個NSProgress的變量 - uploadProgress和downloadProgress,給session task添加了兩個KVO事件
    [delegate setupProgressForTask:task];
    [self addNotificationObserverForTask:task];
    [self.lock unlock];
}

- (void)setupProgressForTask:(NSURLSessionTask *)task {
        ...
            //給task和progress添加kvo
    [task addObserver:self
           forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))
              options:NSKeyValueObservingOptionNew
              context:NULL];
    [task addObserver:self
           forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))
              options:NSKeyValueObservingOptionNew
              context:NULL];

    [task addObserver:self
           forKeyPath:NSStringFromSelector(@selector(countOfBytesSent))
              options:NSKeyValueObservingOptionNew
              context:NULL];
    [task addObserver:self
           forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToSend))
              options:NSKeyValueObservingOptionNew
              context:NULL];
        ...

}

//在KVO回調方法把結果回調出去
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
    ...
            if (self.downloadProgressBlock) {
            self.downloadProgressBlock(object);
        }
    ...
            if (self.uploadProgressBlock) {
            self.uploadProgressBlock(object);
        }
    ...
}

複製代碼

從以上能夠看出:session是惟一的 request和task和delegate是一一對應的,delegate承擔着數據處理並轉發的功能。

該模塊中的一些重點的功能:

//-若是完成後須要作複雜(耗時)的處理,能夠選擇異步隊列
//-若是完成後直接更新UI,能夠選擇主隊列
// [NSOperationQueue mainQueue]

 self.operationQueue = [[NSOperationQueue alloc] init];
 self.operationQueue.maxConcurrentOperationCount = 1;
 self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
複製代碼

delegate在哪一個OperationQueue回調,若是咱們將其設置爲[NSOperationQueue mainQueue]就能在主線程進行回調很是的方便。可是不能把耗時操做放在主線程,因此建立了一個異步的串行隊列。

//delegate= value taskid = key
    self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
    
    //使用NSLock確保mutableTaskDelegatesKeyedByTaskIdentifier線程安全
    self.lock = [[NSLock alloc] init];
    self.lock.name = AFURLSessionManagerLockName;
    
    //再字典的set get remove中加鎖,確保線程安全
    
    
    
    
複製代碼

思考🤔: NSlock能夠不能夠換成信號量,信號量的效率會高一點

#define LOCK(lock) dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
#define UNLOCK(lock) dispatch_semaphore_signal(lock);
@property (nonatomic, strong, nonnull) dispatch_semaphore_t weakCacheLock;

.....

- (void)setObject:(id)obj forKey:(id)key cost:(NSUInteger)g {
    ...
        LOCK(self.weakCacheLock);
        [self.weakCache setObject:obj forKey:[[key mutableCopy] copy]];
        UNLOCK(self.weakCacheLock);
    ...
}

- (void)removeObjectForKey:(id)key {
    ...
        // Remove weak cache
        LOCK(self.weakCacheLock);
        [self.weakCache removeObjectForKey:key];
        UNLOCK(self.weakCacheLock);
}

- (id)objectForKey:(id)key {
    ...
        LOCK(self.weakCacheLock);
        obj = [self.weakCache objectForKey:key];
        UNLOCK(self.weakCacheLock);
    ...
}


複製代碼

而另外一款優秀的圖片庫SDWebImage中用信號量保證線程的安全

[task addObserver:self
           forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))
              options:NSKeyValueObservingOptionNew
              context:NULL];
    [task addObserver:self
           forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))
              options:NSKeyValueObservingOptionNew
              context:NULL];

    [task addObserver:self
           forKeyPath:NSStringFromSelector(@selector(countOfBytesSent))
              options:NSKeyValueObservingOptionNew
              context:NULL];
    [task addObserver:self
           forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToSend))
              options:NSKeyValueObservingOptionNew
              context:NULL];
複製代碼

反射機制(對於任意一個類,都可以知道這個類的全部屬性和方法;對於任意一個對象,都可以調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱爲反射機制。) 在delegate的setupProgressForTask中運用反射機制給task添加KVO

@implementation _AFURLSessionTaskSwizzling

+ (void)load {
        // 1) 首先構建一個NSURLSession對象session,再經過session構建出一個_NSCFLocalDataTask變量
        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
        // 2) 獲取到af_resume實現的指針
        IMP originalAFResumeIMP = method_getImplementation(class_getInstanceMethod([self class], @selector(af_resume)));
        Class currentClass = [localDataTask class];
        // 3) 檢查當前class是否實現了resume。若是實現了,繼續第4步
        while (class_getInstanceMethod(currentClass, @selector(resume))) {
            // 4) 獲取到當前class的父類(superClass)
            Class superClass = [currentClass superclass];
            // 5) 獲取到當前class對於resume實現的指針
            IMP classResumeIMP = method_getImplementation(class_getInstanceMethod(currentClass, @selector(resume)));
           //  6) 獲取到父類對於resume實現的指針
            IMP superclassResumeIMP = method_getImplementation(class_getInstanceMethod(superClass, @selector(resume)));
           // 7) 若是當前class對於resume的實現和父類不同(相似iOS7上的狀況),而且當前class的resume實現和af_resume不同,才進行method swizzling。
            if (classResumeIMP != superclassResumeIMP &&
                originalAFResumeIMP != classResumeIMP) {
                [self swizzleResumeAndSuspendMethodForClass:currentClass];
            }
            // 8) 設置當前操做的class爲其父類class,重複步驟3~8
            currentClass = [currentClass superclass];
        }
        
        [localDataTask cancel];
        [session finishTasksAndInvalidate];
    }
}

...

- (void)af_resume {
    NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state");
    NSURLSessionTaskState state = [self state];
    [self af_resume];
    // 由於通過method swizzling後,此處的af_resume其實就是以前的resume,因此此處調用af_resume就是調用系統的resume。可是在程序中咱們仍是得使用resume,由於其實際調用的是af_resume
    // 若是以前是其餘狀態,就變回resume狀態,此處會通知調用taskDidResume
    if (state != NSURLSessionTaskStateRunning) {
        [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidResumeNotification object:self];
    }
}

...

@end

複製代碼

其實看類的名字就能夠知道是使用methodswizzle這種黑魔法實現AOP模式 經過調用resume,發起網絡請求,同時發送一個通知到sessionManamer,由它統一發送一個狀態到UIKIT類裏面,來改變視圖的變化。

相關文章
相關標籤/搜索