iOS源碼解析—AFNetworking(URLConnection)

概述

AFNetwokring目前是3.x版本,基于NSURLSession的功能進(jìn)行封裝,而2.x版本是基于NSURLConnection。由于NSURLConnection逐漸被NSURLSession所取代,2.x版本逐漸被3.x取代。本篇分析一下2.x版本,因?yàn)樵摪姹旧婕暗囊恍┐a值得學(xué)習(xí),例如NSOperation、KVO的使用。

AFHTTPRequestOperationManager

AFHTTPRequestOperationManager是AFN封裝的管理HTTP請求的類,首先初始化方法中設(shè)置了一些參數(shù)值,代碼注釋如下:

- (instancetype)initWithBaseURL:(NSURL *)url {
    ...
    self.baseURL = url;
    self.requestSerializer = [AFHTTPRequestSerializer serializer]; //序列化
    self.responseSerializer = [AFJSONResponseSerializer serializer];//反序列化
    self.securityPolicy = [AFSecurityPolicy defaultPolicy]; //默認(rèn)的安全策略
    self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];//監(jiān)聽網(wǎng)絡(luò)狀態(tài)
    self.operationQueue = [[NSOperationQueue alloc] init]; //任務(wù)隊(duì)列
    self.shouldUseCredentialStorage = YES;
    return self;
}

初始化方法設(shè)置了請求報(bào)文序列化/反序列化對象,以及默認(rèn)的安全策略,網(wǎng)絡(luò)監(jiān)聽對象,任務(wù)隊(duì)列。

AFHTTPRequestOperationManager提供了一系列HTTP請求相關(guān)的方法,例如GET、POST、PATCH等,內(nèi)部實(shí)現(xiàn)相同,只是method參數(shù)值不同,以GET請求方法為例,代碼注釋如下:

- (AFHTTPRequestOperation *)GET:(NSString *)URLString
                     parameters:(id)parameters
                        success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
                        failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
{
    AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithHTTPMethod:@"GET" URLString:URLString parameters:parameters success:success failure:failure];
    [self.operationQueue addOperation:operation];
    return operation;
}

首先創(chuàng)建一個AFHTTPRequestOperation類型的NSOperation對象,然后將NSOperation對象加入operationQueue隊(duì)列中,開始執(zhí)行operation。在創(chuàng)建AFHTTPRequestOperation對象的方法中,首先通過requestSerializer構(gòu)建NSURLRequest對象,然后設(shè)置相關(guān)屬性,設(shè)置completionBlock,代碼注釋如下:

- (AFHTTPRequestOperation *)HTTPRequestOperationWithRequest:(NSURLRequest *)request
                                                    success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
                                                    failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
{
    AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
    ... //設(shè)置相關(guān)屬性
    [operation setCompletionBlockWithSuccess:success failure:failure]; //設(shè)置operation結(jié)束時的completionBlock
    operation.completionQueue = self.completionQueue; //執(zhí)行completionBlock的隊(duì)列
    operation.completionGroup = self.completionGroup; //執(zhí)行completionBlock的group
    return operation;
}

AFHTTPRequestOperation繼承AFURLConnectionOperation,AFURLConnectionOperation真正負(fù)責(zé)網(wǎng)絡(luò)請求的發(fā)出以及處理,AFHTTPRequestOperation設(shè)置completionBlock的方法如下:

- (void)setCompletionBlockWithSuccess:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
                              failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-retain-cycles"
#pragma clang diagnostic ignored "-Wgnu"
    self.completionBlock = ^{
        if (self.completionGroup) {
            dispatch_group_enter(self.completionGroup);
        }
        //在異步隊(duì)列中執(zhí)行
        dispatch_async(http_request_operation_processing_queue(), ^{
            if (self.error) {
                if (failure) { //網(wǎng)絡(luò)請求失敗
                    //在completionQueue或者主線程隊(duì)列中執(zhí)行失敗block
                    dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{
                        failure(self, self.error);
                    });
                }
            } else { //網(wǎng)絡(luò)請求成功
                //反序列化報(bào)文數(shù)據(jù)
                id responseObject = self.responseObject;
                if (self.error) {
                    if (failure) {
                        //反序列化報(bào)文數(shù)據(jù)失敗,在completionQueue或者主線程隊(duì)列中執(zhí)行失敗block
                        dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{
                            failure(self, self.error);
                        });
                    }
                } else {
                    if (success) {
                        //反序列化報(bào)文數(shù)據(jù)成功,在completionQueue或者主線程隊(duì)列中執(zhí)行成功block
                        dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{
                            success(self, responseObject);
                        });
                    }
                }
            }
            if (self.completionGroup) {
                dispatch_group_leave(self.completionGroup);
            }
        });
    };
#pragma clang diagnostic pop
}

該方法定義一個void (^)(void)類型的block,設(shè)置給父類的completionBlock屬性,當(dāng)執(zhí)行completionBlock時,首先會判斷網(wǎng)絡(luò)請求是否錯誤,如果出錯,直接在completionQueue或者主線程中調(diào)用failure的block拋給調(diào)用層。如果網(wǎng)絡(luò)請求成功,則調(diào)用responseObject方法反序列化響應(yīng)報(bào)文數(shù)據(jù)responseData,代碼注釋如下:

- (id)responseObject {
    [self.lock lock];
    if (!_responseObject && [self isFinished] && !self.error) {
        NSError *error = nil;
        //反序列化響應(yīng)報(bào)文數(shù)據(jù)
        self.responseObject = [self.responseSerializer responseObjectForResponse:self.response data:self.responseData error:&error];
        if (error) {
            self.responseSerializationError = error;
        }
    }
    [self.lock unlock];
    return _responseObject;
}

由于該方法在http_request_operation_processing_queue()中執(zhí)行,不影響主線程的性能。如果反序列化成功,調(diào)用success的blcok將error拋給調(diào)用層,如果失敗,調(diào)用failure的block將反序列化后的對象拋給調(diào)用層。

AFURLConnectionOperation

AFURLConnectionOperation負(fù)責(zé)發(fā)送網(wǎng)絡(luò)請求,處理delegate回調(diào)方法。AFURLConnectionOperation繼承NSOperation,眾所周知,當(dāng)實(shí)現(xiàn)一個自定義的NSOperation時,需要重寫NSOperation的相關(guān)方法,以確保operation機(jī)制的正常運(yùn)行。

初始化方法

初始化方法設(shè)置了相關(guān)參數(shù),代碼注釋如下:

- (instancetype)initWithRequest:(NSURLRequest *)urlRequest {
    NSParameterAssert(urlRequest);
    self = [super init];
    if (!self) {
        return nil;
    }
    _state = AFOperationReadyState; //狀態(tài)設(shè)置為準(zhǔn)備執(zhí)行
    self.lock = [[NSRecursiveLock alloc] init]; //創(chuàng)建遞歸鎖
    self.lock.name = kAFNetworkingLockName;
    self.runLoopModes = [NSSet setWithObject:NSRunLoopCommonModes];
    self.request = urlRequest; //設(shè)置urlRequest
    self.shouldUseCredentialStorage = YES;
    self.securityPolicy = [AFSecurityPolicy defaultPolicy]; //設(shè)置securityPolicy
    return self;
}

該方法設(shè)置用于網(wǎng)絡(luò)請求的request,初始化了遞歸鎖,安全策略對象securityPolicy,同時設(shè)置了狀態(tài)為AFOperationReadyState(準(zhǔn)備執(zhí)行)。

狀態(tài)機(jī)制

當(dāng)operation加入到operationQueue中時,operationQueue會通過KVO的方式監(jiān)聽operation的狀態(tài),NSOperation有幾種狀態(tài),分別對應(yīng)以下屬性:

isReady(是否準(zhǔn)備執(zhí)行)

isExecuting(是否正在執(zhí)行)

isCancelled(是否取消)

isPaused(是否暫停)

isFinished(是否完成)

上面的屬性狀態(tài)決定operation的生命周期,operationQueue監(jiān)聽operation的狀態(tài)屬性,當(dāng)operation的isFinished屬性為YES時,說明operation生命周期結(jié)束,operation會在隊(duì)列中被釋放。AFURLConnectionOperation實(shí)現(xiàn)了自定義的operation,重寫了以下屬性方法:

- (BOOL)isReady {
    return self.state == AFOperationReadyState && [super isReady];
}
- (BOOL)isExecuting {
    return self.state == AFOperationExecutingState;
}
- (BOOL)isFinished {
    return self.state == AFOperationFinishedState;
}

通過getter方法訪問operation的屬性時,返回的狀態(tài)值會根據(jù)AFNetworking維護(hù)的枚舉值來確定。下面是枚舉類型AFOperationState的代碼:

typedef NS_ENUM(NSInteger, AFOperationState) {
    AFOperationPausedState      = -1, //暫停
    AFOperationReadyState       = 1, //準(zhǔn)備執(zhí)行
    AFOperationExecutingState   = 2, //正在執(zhí)行
    AFOperationFinishedState    = 3, //完成
};

分別對應(yīng)operation的狀態(tài)屬性,同時實(shí)現(xiàn)-setState:方法來更新AFOperationState的枚舉值,下面是代碼注釋:

- (void)setState:(AFOperationState)state {
    if (!AFStateTransitionIsValid(self.state, state, [self isCancelled])) {
        return;
    }
    [self.lock lock];
    NSString *oldStateKey = AFKeyPathFromOperationState(self.state); //原狀態(tài)
    NSString *newStateKey = AFKeyPathFromOperationState(state); //新狀態(tài)
    [self willChangeValueForKey:newStateKey]; //手動發(fā)送KVO通知
    [self willChangeValueForKey:oldStateKey]; //手動發(fā)送KVO通知
    _state = state; //切換狀態(tài)
    [self didChangeValueForKey:oldStateKey]; //手動發(fā)送KVO通知
    [self didChangeValueForKey:newStateKey]; //手動發(fā)送KVO通知
    [self.lock unlock];
}

首先通過AFKeyPathFromOperationState方法將新舊AFOperationState枚舉值映射成operation的狀態(tài)屬性名,然后更新AFOperationState枚舉值,當(dāng)外界訪問operation的狀態(tài)屬性時,狀態(tài)已經(jīng)改變。同時手動發(fā)送KVO通知,通知operationQueue,operation的狀態(tài)屬性發(fā)生了改變。

start方法

如果實(shí)現(xiàn)自定義的NSOperation,需要重寫start方法,下面是代碼注釋:

- (void)start {
    [self.lock lock];
    if ([self isCancelled]) { //如果operation之前被取消,調(diào)用cancelConnection方法取消connection
        [self performSelector:@selector(cancelConnection) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
    } else if ([self isReady]) { //如果operation準(zhǔn)備執(zhí)行,調(diào)用operationDidStart方法開始構(gòu)建connection,發(fā)送網(wǎng)絡(luò)請求
        self.state = AFOperationExecutingState;
        [self performSelector:@selector(operationDidStart) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
    }
    [self.lock unlock];
}

該方法首先判斷operation當(dāng)前的狀態(tài),如果之前已經(jīng)被取消了,則調(diào)用cancelConnection方法進(jìn)一步處理,該方法放在后文分析。如果新建operation,在初始化方法中,設(shè)置初始狀態(tài)是AFOperationReadyState,即operation的狀態(tài)isReady=YES,調(diào)用operationDidStart方法開始進(jìn)行網(wǎng)絡(luò)請求。同時AFNetworking創(chuàng)建了一個常駐線程來執(zhí)行connection相關(guān)的方法。常駐線程由類方法networkRequestThread創(chuàng)建,代碼如下:

+ (NSThread *)networkRequestThread {
    static NSThread *_networkRequestThread = nil;
    static dispatch_once_t oncePredicate;
    dispatch_once(&oncePredicate, ^{
        _networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil]; //創(chuàng)建常駐線程
        [_networkRequestThread start];
    });
    return _networkRequestThread;
}
+ (void)networkRequestThreadEntryPoint:(id)__unused object {
    @autoreleasepool {
        [[NSThread currentThread] setName:@"AFNetworking"];
        NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
        [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode]; //為runloop添加port,使runloop永不退出
        [runLoop run];
    }
}

首先在dispatch_once()中創(chuàng)建一個線程,由于dispatch_once中的block只會執(zhí)行一次,所以線程只會創(chuàng)建一次,且_networkRequestThread是static類型的,所以會常駐內(nèi)存不被釋放。每次調(diào)用networkRequestThread方法都會返回該線程指針。由于是手動創(chuàng)建的子線程,需要手動開啟它的runloop,并且在runloop中添加port,使其永不退出。我們將這個常駐線程稱為AFN線程。在AFN線程中執(zhí)行operationDidStart方法,下面是代碼注釋:

- (void)operationDidStart {
    [self.lock lock];
    if (![self isCancelled]) {
        self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO];
        NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
        for (NSString *runLoopMode in self.runLoopModes) {
            [self.connection scheduleInRunLoop:runLoop forMode:runLoopMode];
            [self.outputStream scheduleInRunLoop:runLoop forMode:runLoopMode];
        }
        [self.outputStream open];
        [self.connection start];
    }
    [self.lock unlock];
    dispatch_async(dispatch_get_main_queue(), ^{
        [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingOperationDidStartNotification object:self];
    });
}

首先創(chuàng)建了connection,設(shè)置當(dāng)前operation對象為connection對象的delegate,處理網(wǎng)絡(luò)請求回調(diào)的各個方法。如果startImmediately參數(shù)為YES,connection會立刻啟動,開始下載數(shù)據(jù),且connection在當(dāng)前線程的runloop中執(zhí)行,如果為NO,暫不開始請求數(shù)據(jù),需要調(diào)用start方法手動開始,同時調(diào)用scheduleInRunLoop:forMode:方法把NSURLConnection加入到指定線程的run loop中去運(yùn)行,否則會加入當(dāng)前線程的runloop中去,使用outputStream來接收網(wǎng)絡(luò)請求回來的數(shù)據(jù)。

NSURLConnectionDelegate

在connection網(wǎng)絡(luò)請求的過程中,將回調(diào)方法拋給delegate執(zhí)行,下面分析一下主要方法:

  1. -(void)connection:willSendRequestForAuthenticationChallenge:方法

    當(dāng)客戶端發(fā)送HTTPS請求給服務(wù)端時,會進(jìn)行SSL握手,在握手的過程中,服務(wù)端需要客戶端進(jìn)行授權(quán)的響應(yīng),客戶端對服務(wù)端發(fā)來的信息進(jìn)行校驗(yàn),在iOS代碼中,抽象為系統(tǒng)拋出delegate方法給上層代碼,同時傳入一個challenge對象,封裝了需要驗(yàn)證的信息,下面是代碼部分注釋:

    - (void)connection:(NSURLConnection *)connection
    willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
    {
        ...
        if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) 
        {
         //對serverTrust對象和host進(jìn)行校驗(yàn)
         if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
                 //校驗(yàn)通過,生成一個憑證對象credential
                NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
                 //使用credential給系統(tǒng)
                [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
            } else {
                 //驗(yàn)證失敗,取消后續(xù)SSL連接
                [[challenge sender] cancelAuthenticationChallenge:challenge];
            }
        } else {
            if ([challenge previousFailureCount] == 0) {
                if (self.credential) { //直接用現(xiàn)有的credential給系統(tǒng)
                    [[challenge sender] useCredential:self.credential forAuthenticationChallenge:challenge];
                } else {
                    [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
                }
            } else {
                [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
            }
        }
    }
    

    challenge中的屬性protectionSpace是一個NSURLProtectionSpace對象,包含服務(wù)器的host、port、isProxy等信息,同時包含authenticationMethod,即校驗(yàn)方式的類型,如果類型是NSURLAuthenticationMethodServerTrust,則驗(yàn)證信任對象serverTrust,如果驗(yàn)證通過,生成一個憑證對象credential返回給服務(wù)端,具體的流程可以參考騰訊Bugly的文章。

  2. -(void)connection: didReceiveData:

    當(dāng)網(wǎng)絡(luò)連接建立后,服務(wù)器開始向客戶端傳輸數(shù)據(jù),系統(tǒng)會回調(diào)該方法,上層代碼負(fù)責(zé)接收并且拼裝response數(shù)據(jù)。下面是部分代碼注釋:

    - (void)connection:(NSURLConnection __unused *)connection
        didReceiveData:(NSData *)data
    {
        NSUInteger length = [data length]; //需要讀取的字節(jié)長度
        while (YES) {
            NSInteger totalNumberOfBytesWritten = 0; //本次一共寫入的字節(jié)長度
            if ([self.outputStream hasSpaceAvailable]) { //outputStream有空間寫入
                const uint8_t *dataBuffer = (uint8_t *)[data bytes];
                NSInteger numberOfBytesWritten = 0;
                while (totalNumberOfBytesWritten < (NSInteger)length) {
                    numberOfBytesWritten = [self.outputStream write:&dataBuffer[(NSUInteger)totalNumberOfBytesWritten] maxLength:(length - (NSUInteger)totalNumberOfBytesWritten)]; //將dataBuffer中的數(shù)據(jù)寫入outputStream中
                    if (numberOfBytesWritten == -1) {
                        break;
                    }
                    totalNumberOfBytesWritten += numberOfBytesWritten; //累加寫入的字節(jié)長度
                }
                break;
            } else { //outputStream沒有空間寫入
                [self.connection cancel];
                if (self.outputStream.streamError) {
                    [self performSelector:@selector(connection:didFailWithError:) withObject:self.connection withObject:self.outputStream.streamError];
                }
                return;
            }
        }
        ...
    }
    

    該方法主要將data數(shù)據(jù)寫入outputStream中,對于本次數(shù)據(jù)data,如果一次性寫不全進(jìn)outputStream,則通過totalNumberOfBytesWritten記錄共寫入的字節(jié)長度,通過while循環(huán)控制,直到全部寫入。outputStream通過[NSOutputStream outputStreamToMemory]創(chuàng)建,是寫入內(nèi)存的流對象,如果hasSpaceAvailable返回NO,即后續(xù)返回的response數(shù)據(jù)沒有空間存放,則直接斷開網(wǎng)絡(luò)請求。

  3. -(void)connectionDidFinishLoading:

    當(dāng)網(wǎng)絡(luò)請求結(jié)束時,調(diào)用該方法,獲取最終的response數(shù)據(jù),結(jié)束本次operation。

    - (void)connectionDidFinishLoading:(NSURLConnection __unused *)connection {
        self.responseData = [self.outputStream propertyForKey:NSStreamDataWrittenToMemoryStreamKey]; //獲取response數(shù)據(jù)
        [self.outputStream close]; //關(guān)閉outputStream
        if (self.responseData) {
           self.outputStream = nil;
        }
        self.connection = nil;
        [self finish]; //結(jié)束operation
    }
    
  4. -(NSCachedURLResponse *)connection: willCacheResponse:

    如果服務(wù)端需要將response數(shù)據(jù)緩存到客戶端的NSURLCache緩存系統(tǒng),在緩存到客戶端本地之前,會首先調(diào)用該方法,可以修改緩存的數(shù)據(jù),默認(rèn)是接口返回的response數(shù)據(jù)。

    - (NSCachedURLResponse *)connection:(NSURLConnection *)connection
                      willCacheResponse:(NSCachedURLResponse *)cachedResponse
    {
        if (self.cacheResponse) {
            return self.cacheResponse(connection, cachedResponse); //修改服務(wù)端返回緩存數(shù)據(jù)
        } else {
            if ([self isCancelled]) {
                return nil;
            }
            return cachedResponse;
        }
    }
    

    默認(rèn)網(wǎng)絡(luò)請求的緩存策略是UseProtocolCachePolicy,根據(jù)服務(wù)端返回的Cache-Control字段來開啟HTTP緩存功能,字段值可能包含 max-age,是公共 public 還是私有 private,或者不緩存no-cache 等信息。關(guān)于NSURLCache的相關(guān)講解,可以參考這篇文章

    - (NSCachedURLResponse *)connection:(NSURLConnection *)connection
                      willCacheResponse:(NSCachedURLResponse *)cachedResponse
    {
        if (self.cacheResponse) {
            return self.cacheResponse(connection, cachedResponse); //修改需要緩存的數(shù)據(jù)
        } else {
            if ([self isCancelled]) {
                return nil;
            }
            return cachedResponse;
        }
    }
    
控制生命周期

上文所述,AFURLConnectionOperation通過setState:方法實(shí)現(xiàn)了operation狀態(tài)的切換,從而控制operation的生命周期。下面分析一下,另外幾個方法:

  1. finish方法

    當(dāng)網(wǎng)絡(luò)請求結(jié)束時,該方法被調(diào)用,負(fù)責(zé)結(jié)束operation的生命周期:

    - (void)finish {
        [self.lock lock];
        self.state = AFOperationFinishedState; //設(shè)置結(jié)束狀態(tài),結(jié)束operation的生命周期
        [self.lock unlock];
        ...
    }
    

    在setState方法里更改為AFOperationFinishedState狀態(tài)并且手動觸發(fā)KVO,通知operationQueue,isFinished屬性變化,觸發(fā)completionBlock,執(zhí)行block里面的代碼。

  2. cancel方法

    該方法取消一個cancel這個operation,同時調(diào)用cancelConnection方法取消當(dāng)前的connection連接。

    - (void)cancel {
        [self.lock lock];
        if (![self isFinished] && ![self isCancelled]) {
            [super cancel]; //調(diào)用NSOperation的cancel方法
            if ([self isExecuting]) {
                [self performSelector:@selector(cancelConnection) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
            }
        }
        [self.lock unlock];
    }
    

    注意cancelConnection方法也會在start方法中調(diào)用,新建一個請求時,當(dāng)發(fā)現(xiàn)這個operation之前被取消了,進(jìn)一步判斷connection是否建立,如果connection存在,先取消connection,然后調(diào)用finish方法,結(jié)束operation的生命周期。

  3. pause方法和resume方法

    AFURLConnectionOperation提供了pause和resume方法,pause方法將狀態(tài)改為AFOperationPausedState,同時取消當(dāng)前的connection。resume方法將狀態(tài)重新改為AFOperationReadyState,同時調(diào)用start方法,重新請求connection,將狀態(tài)改為AFOperationExecutingState。下面是代碼注釋:

    - (void)pause {
        if ([self isPaused] || [self isFinished] || [self isCancelled]) {
            return;
        }
        [self.lock lock];
        if ([self isExecuting]) { //取消當(dāng)前conneciton
            [self performSelector:@selector(operationDidPause) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
     ...
        self.state = AFOperationPausedState; //暫停operation
        [self.lock unlock];
    }
      
    - (void)resume {
        if (![self isPaused]) {
            return;
        }
        [self.lock lock];
        self.state = AFOperationReadyState; //operation重置為isReady狀態(tài)
        [self start]; //新建connection,重新請求數(shù)據(jù),狀態(tài)職位isExecuting
        [self.lock unlock];
    }
    

    pause方法只是取消本次網(wǎng)絡(luò)請求,不會結(jié)束operation的生命周期,當(dāng)外界調(diào)用resume方法時,也只是重新進(jìn)行網(wǎng)絡(luò)請求。

小結(jié)

雖然NSURLConnection及其基礎(chǔ)上封裝的AF2.x版本逐漸被廢棄,但是作者關(guān)于operation的使用,以及如何實(shí)現(xiàn)一個網(wǎng)絡(luò)請求的處理流程,對于初學(xué)者來說,具有參考和學(xué)習(xí)的價值。

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

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