AFNetwork分析

前面將NSRunloop以及NSOperation需要用到的基本知識學習理解了一下,這下終於要到我這兩個月都想幹的事情了,剖析一個網絡framework,可惜的是asi實在過於龐大複雜了,精力不足,沒有辦法去滲透分析一個5000行的文件,還有對cfnetwork可能也不是很熟悉,考慮了一下,暫時還是放棄asi。先分析afnetwork,其實腦袋中現在已經有了對這個框架執行流程的大概步驟,發覺這個框架寫的其實挺簡單但是設計的很巧妙,再實際的頁面請求數據時,可能反應沒有asi那麼迅速,如果在使用block的話感覺會更明顯的。這個框架大量使用了gcd,所以使用起來非常方便容易上手,裏面對gcd的使用也是可以借鑑的。

============================================================

再github上下載了AFNetwork的網絡包,裏面的使用流程我已經看了一遍,也知道是怎麼回事了,但是很鬱悶,不知道如何下手寫文章。那就按照官方demo的執行流程來寫吧。框架中有很多的網絡請求類,這些請求類的積累都是AFURLConnectionOperation。下面開始寫在demo中的controller請求數據的流程。

首先是使用Post對象中寫了一個類方法,這個方法用來請求數據,通過block來傳遞數據請求返回之後的後續操作。再Post對象中實現如下:

 

+ (void)globalTimelinePostsWithBlock:(void (^)(NSArray *posts, NSError *error))block

{

    [[AFAppDotNetAPIClient sharedClient] getPath:@"stream/0/posts/stream/global" parameters:nilsuccess:^(AFHTTPRequestOperation *operation, id JSON) {

        NSArray *postsFromResponse = [JSON valueForKeyPath:@"data"];

        NSMutableArray *mutablePosts = [NSMutableArray arrayWithCapacity:[postsFromResponse count]];

        for (NSDictionary *attributes in postsFromResponse) {

            Post *post = [[Post allocinitWithAttributes:attributes];

            [mutablePosts addObject:post];

        }

        if (block) {

  block([NSArray arrayWithArray:mutablePosts], nil);

        }

    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {

        if (block) {

            block([NSArray array], error);

        }

    }];

}

再這個類方法中,又是通過調用AFAppDotNetAPIClient中的類方法來加載具體數據的,當具體數據加載完成之後,就直接使用傳遞進來的block參數直接更新UI線程中的數據。而在AFAppDotNetAPIClient中的類方法的實現如下:

 

- (void)getPath:(NSString *)path

     parameters:(NSDictionary *)parameters

        success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success

        failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure

{

NSURLRequest *request = [self requestWithMethod:@"GET" path:path parameters:parameters];

    AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithRequest:request success:successfailure:failure];

    [self enqueueHTTPRequestOperation:operation];

}

可以看出,最後真正請求數據的方法是AFHTTPRequestOperation,operation再初始化時會傳遞進success/failure兩個block參數來講完成請求之後的數據操作返回。關於enqueueHTTPRequestOperation方法實際就是講operation加入到self.operationQueue中,然後就開始通過這個queue來管理網絡請求操作行爲,如果這個時候queue正在進行的operation的數量沒有到達setMaxConcurrentOperationCount參數的設定,operation就會直接開始,如果到達了就會等待,總之這個時候operation合適開始合適結束的管理已經交給了queue。再來看一下queue的這個參數,是NSOperationQueueDefaultMaxConcurrentOperationCount ,也就是說這個operationqueue對所有的operation都是併發進行請求的,沒有最大的請求數量的限制,operation進入queue就會開始執行。

到這裏就可以發現,其實afnetwork中所有的請求都是異步的,並不支持同步請求,異步operation即支持併發的operation的實現比同步operation的實現有難度一些,而且由於裏面的操作同時又都是異步的,不能直接使用自帶的兩種operation,只能自己去實現特定的operation,主要就是分析AFURLConnectionOperation的實現,等分析完成之後再分析AFHTTPRequestOperation以及AFHTTPClient的結構。

AFURLConnectionOperation的分析如下:

 

- (id)initWithRequest:(NSURLRequest *)urlRequest {

    self = [super init];

    if (!self) {

return nil;

    }

    self.lock = [[NSRecursiveLock allocinit]; // 用於對屬性更改時的同步鎖,防止出現多線程下的情況

    self.lock.name = kAFNetworkingLockName; // 同步鎖的名稱

   self.runLoopModes = [NSSet setWithObject:NSRunLoopCommonModes]; // 設置runloop的mode,用於後面處理返回數據時的runloop

     self.request = urlRequest;  // url地址

    self.outputStream = [NSOutputStream outputStreamToMemory]; // outputstream,用於將網絡請求返回的數據緩衝到memory中

    self.state = AFOperationReadyState; // 更改operation的state,由於重寫setState方法,其中會對實現併發operation所需要實現的一些屬性進行設置

 

    return self;

}

其中需要注意的時關於setState方法的使用

 

- (void)setState:(AFOperationState)state {

    [self.lock lock];

    if (AFStateTransitionIsValid(self.state, state, [self isCancelled])) {

        NSString *oldStateKey = AFKeyPathFromOperationState(self.state);

        NSString *newStateKey = AFKeyPathFromOperationState(state);

        [self willChangeValueForKey:newStateKey];

        [self willChangeValueForKey:oldStateKey];

        _state = state;

        [self didChangeValueForKey:oldStateKey];

        [self didChangeValueForKey:newStateKey];        

        switch (state) {

            case AFOperationExecutingState:

                [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingOperationDidStartNotification object:self];

                break;

            case AFOperationFinishedState:

                [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingOperationDidFinishNotification object:self];

                break;

            default:

                break;

        }

    }

    [self.lock unlock];

}

這個裏面尤其需要的是AFKeyPathFromOperationState,通過state獲得一個string,這個string表示一些Operation中定義的KVO性質,如isReady/isExecuting/isFinished/isPaused等關鍵字的keypath,通過這些屬性來設置operation的執行狀態,我的猜想是operation通過這些KVO性質來通知operationqueue這些屬性發生變化,然後在重寫的- (BOOL)isReady  等方法中,通過自己定義的這些state返回不同的bool值,這樣就達到了控制operation的狀態的作用。比如

 

- (BOOL)isReady {

    return self.state == AFOperationReadyState && [super isReady];

}

設置好了到readystate,接下來operationqueue會在合適的時候調用start方法,開始執行operation

 

- (void)start {

    [self.lock lock];

    if ([self isReady]) {

        self.state = AFOperationExecutingState;

        [self performSelector:@selector(operationDidStart) onThread:[[self class] networkRequestThread]withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];

    }

    [self.lock unlock];

}

在start方法中,同樣是通過設置setState方法來設置各種kvo的屬性值來向queue通知operation狀態的變化,以及通過state來確定isExcuting方法返回的bool值,這裏面又用到了同步鎖。這裏面可以看到沒有實現main方法,實現的是併發的operation,直接使用perform來在某個thread中執行operationDidstart方法,即開始執行任務,也就是非併發operation中的main做的事情。

這裏面最重要的地方是通過[[self class] networkRequestThread]獲取一個class共有的thread,由於使用的NSURLRequest是一個異步方法,處理數據量耗費不是很大,只用在數據返回時進行一下處理即可,所以沒有必要爲每一個網絡請求起一個線程來運行runloop等待數據返回之後進行處理,對所有的網絡請求數據返回的處理只需要一個thread即可,這個thread維護一個runloop來處理所有返回數據。

隨後分析- (void)operationDidStart方法的實現,分析如何安排處理返回的網絡數據

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章