iOS開發 網絡框架AFNetworking源碼(一)

    目前iOS開發中使用最多的網絡訪問框架就是AFNetworking了。做爲一個第三方框架,用起來確實比直接使用iOS自帶的要方便得多。node

    AFNetworking在github上能夠直接下載。地址爲:https://github.com/AFNetworking/AFNetworking 。git

    首先先看AFURLConnectionOperation類,繼承自NSOperation。github

@interface
 AFURLConnectionOperation : 
NSOperation

    在這裏構建了NSURLConnection,做爲NSURLConnection的delegate處理請求回調,作好狀態切換,線程管理,能夠說是AFNetworking最核心的類。安全

    1.1這裏用枚舉定義了網絡訪問狀態。網絡

typedef NS_ENUM(NSInteger, AFOperationState) {
    AFOperationPausedState      = -1,
    AFOperationReadyState       = 1,
    AFOperationExecutingState   = 2,
    AFOperationFinishedState    = 3,
};

1.2爲了保證線程安全,全部的單例都使用了dispatch_once,保證了其只執行一次,這種寫法在iOS代碼中隨處可見,很普及。併發

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, ^{
    //這裏的DISPATCH_QUEUE_CONCURRENT生成一個併發執行隊列
        af_url_request_operation_completion_queue = dispatch_queue_create("com.alamofire.networking.operation.queue", DISPATCH_QUEUE_CONCURRENT );
    });
    return af_url_request_operation_completion_queue;
}

    1.3大量使用了: typedef 原變量類型 別名app

    舉個例子:typedef char(*pFun)(int)      使用時候:(*pFun)(2); 是否是很容易看懂?框架

    做者是這樣子寫的:異步

typedef void (^AFURLConnectionOperationProgressBlock)(NSUInteger bytes, long long totalBytes, long long totalBytesExpected);
typedef void (^AFURLConnectionOperationAuthenticationChallengeBlock)(NSURLConnection *connection, NSURLAuthenticationChallenge *challenge);
typedef NSCachedURLResponse * (^AFURLConnectionOperationCacheResponseBlock)(NSURLConnection *connection, NSCachedURLResponse *cachedResponse);
typedef NSURLRequest * (^AFURLConnectionOperationRedirectResponseBlock)(NSURLConnection *connection, NSURLRequest *request, NSURLResponse *redirectResponse);

    你會看到在變量聲明的時候,聲明的block真心看起來很清爽,是否是?
async

@property (readwrite, nonatomic, copy) AFURLConnectionOperationCacheResponseBlock cacheResponse;
@property (readwrite, nonatomic, copy) AFURLConnectionOperationRedirectResponseBlock redirectResponse;

    1.4在代碼塊中避免循環應用:

- (void)setShouldExecuteAsBackgroundTaskWithExpirationHandler:(void (^)(void))handler {
    [self.lock lock];
    if (!self.backgroundTaskIdentifier) {
        UIApplication *application = [UIApplication sharedApplication];
        __weak __typeof(self)weakSelf = self;
        self.backgroundTaskIdentifier = [application beginBackgroundTaskWithExpirationHandler:^{
            __strong __typeof(weakSelf)strongSelf = weakSelf;
            if (handler) {
                handler();
            }
            if (strongSelf) {
                [strongSelf cancel];
                [application endBackgroundTask:strongSelf.backgroundTaskIdentifier];
                strongSelf.backgroundTaskIdentifier = UIBackgroundTaskInvalid;
            }
        }];
    }
    [self.lock unlock];
}

weakSelf是爲了block不持有self,避免循環引用,而再聲明一個strongSelf是由於一旦進入block執行,就不容許self在這個執行過程當中釋放。block執行完後這個strongSelf會自動釋放,沒有循環引用問題。

2.一、先看看網絡請求發起方法,是調用了- (void)start開始的,接下來初始化connection連接。

self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO];

初始化完成以後,,調用網絡請求start方法,解開線程鎖,而後回到主線程中發送一個通知給通知中心:

dispatch_async(dispatch_get_main_queue(), ^{
        [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingOperationDidStartNotification object:self];
    });

    看到了嗎?做者發送通知的時候,使用的關鍵字,不是亂寫的,很規範!

NSString * const AFNetworkingOperationDidStartNotification = @"com.alamofire.networking.operation.start";
NSString * const AFNetworkingOperationDidFinishNotification = @"com.alamofire.networking.operation.finish";

2.2 關於代碼中Runloop的操做,網上有一個解析,說得很清楚:

    若直接在主線程調用異步接口,會有個Runloop相關的問題:

    當在主線程調用 [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES] 時,請求發出,偵放任務會加入到主線程的 Runloop 下,RunloopMode 會默認爲 NSDefaultRunLoopMode。這代表只有當前線程的Runloop 處於 NSDefaultRunLoopMode 時,這個任務纔會被執行。但當用戶滾動 tableview 或 scrollview 時,主線程的 Runloop 是處於 NSEventTrackingRunLoopMode 模式下的,不會執行 NSDefaultRunLoopMode 的任務,因此會出現一個問題,請求發出後,若是用戶一直在操做UI上下滑動屏幕,那在滑動結束前是不會執行回調函數的,只有在滑動結束,RunloopMode 切回 NSDefaultRunLoopMode,纔會執行回調函數。蘋果一直把動畫效果性能放在第一位,估計這也是蘋果提高UI動畫性能的手段之一。

 

    因此若要在主線程使用 NSURLConnection 異步接口,須要手動把 RunloopMode 設爲 NSRunLoopCommonModes。這個 mode 意思是不管當前 Runloop 處於什麼狀態,都執行這個任務。

NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO]; 
[connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; 
[connection start];


AFNetworking 用的是在子線程中回調異步接口,建立了一條常駐線程專門處理全部請求的回調事件,這個模型跟 nodejs 有點相似。網絡請求回調處理完,組裝好數據後再給上層調用者回調,這時候回調是拋回主線程的,由於主線程是最安全的,使用者可能會在回調中更新UI,在子線程更新UI會致使各類問題,通常使用者也能夠不須要關心線程問題。

2.3 AFURLConnectionOperation 有一把遞歸鎖,在全部會訪問/修改爲員變量的對外接口都加了鎖,由於這些對外的接口用戶是能夠在任意線程調用的,對於訪問和修改爲員變量的接口,必須用鎖保證線程安全。

    後面這一部分是轉載的。

相關文章
相關標籤/搜索