+ (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 alloc] initWithAttributes: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 alloc] init]; // 用於對屬性更改時的同步鎖,防止出現多線程下的情況
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方法的實現,分析如何安排處理返回的網絡數據