1、AFNetworking的簡單使用
大家使用此開源框架一般是用來進(jìn)行網(wǎng)絡(luò)請求的,下面是一個簡單的例子
//創(chuàng)建一個管理者對象
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
//進(jìn)行超時設(shè)置
manager.requestSerializer.timeoutInterval = 10;
//序列化模式設(shè)置(默認(rèn)是Json)
manager.responseSerializer = [AFJSONResponseSerializer serializer];
//發(fā)送一個GET請求
[manager GET:urlString
parameters:nil
success:^(AFHTTPRequestOperation * _Nonnull operation, id _Nonnull responseObject) {
NSLog(@"加載成功");
}
failure:^(AFHTTPRequestOperation * _Nullable operation, NSError * _Nonnull error) {
NSLog(@"加載失敗");
}];
2、大致框架
該開源框架是基于NSURLConnection 和 NSURLSession 進(jìn)行開發(fā)的。其中有兩個管理類:AFHTTPRequestOperationManager 和 AFHTTPSessionManager。另外一個主要的類是AFURLConnectionOperation,這是一個自定義的NSOperation,用來進(jìn)行網(wǎng)絡(luò)請求任務(wù),回調(diào),傳到主線程。
3、AFURLConnectionOperation
首先討論的是線程。我們進(jìn)行網(wǎng)絡(luò)請求無外乎以下幾點(diǎn):
1)主線程同步請求:這會堵塞主線程,一般沒人采用。
2)主線程調(diào)用異步請求:NSURLConnection 在主線程Runloop觸發(fā)回調(diào)事件,主線程的Mode(?可以理解為狀態(tài)吧)有兩個默認(rèn)的:NSDefaultRunLoopMode,記錄平時的狀態(tài)和UITrackingRunLoopMode,追蹤ScrollView滑動時的狀態(tài)。如果你想隨時隨刻都接受NSURLConnection的回調(diào),就必須將設(shè)置在NSRunLoopCommonModes
狀態(tài)下,因?yàn)槿绻恢碧幱诨瑒訝顟B(tài)的話,就不能處理回調(diào)。但是這樣又會影響動畫效果。
3)子線程調(diào)用同步請求:此時進(jìn)行請求雖然不會影響主線程的狀態(tài),但是處理一條網(wǎng)請求就要占據(jù)一條線程,該線程一直阻塞直到進(jìn)行回調(diào),若請求比較多的話,就會占據(jù)多條線程,資源浪費(fèi)。
4)子線程調(diào)用異步請求:在這種情況下可以設(shè)置一條常駐子線程,用來進(jìn)行網(wǎng)絡(luò)請求的回調(diào),這樣多個網(wǎng)路請求進(jìn)行請求時,回調(diào)都在統(tǒng)一的一個子線程,之后在傳到主線程,也不會影響到主線程。比起上面的方式3),有不小的優(yōu)勢。
該框架采用的就是方式4)。源碼:
+ (void)networkRequestThreadEntryPoint:(id)__unused object {
@autoreleasepool {
[[NSThread currentThread] setName:@"AFNetworking"];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
[runLoop run];
}
}
//創(chuàng)建一個線程 來進(jìn)行網(wǎng)絡(luò)活動
+ (NSThread *)networkRequestThread {
static NSThread *_networkRequestThread = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];
[_networkRequestThread start];
});
return _networkRequestThread;
}
當(dāng)一個operation 鍵入到 queue 中,就會自動調(diào)用operation 的 方法:
- (void)start;
該框架的此方法將會在上面創(chuàng)建的線程中創(chuàng)建一個connection請求網(wǎng)絡(luò)數(shù)據(jù),并在該線程中進(jìn)行回調(diào)。并且于此同時創(chuàng)建了一個outputStream,用于進(jìn)行將收到的數(shù)據(jù)寫進(jìn)內(nèi)存。
- (NSOutputStream *)outputStream {
if (!_outputStream) {
self.outputStream = [NSOutputStream outputStreamToMemory];
}
return _outputStream;
}
補(bǔ)充:
operation 里有包含了一個狀態(tài)機(jī)的東西,描述當(dāng)前operation的狀態(tài),用的是KVO進(jìn)行管理。狀態(tài)分為
isReady → isExecuting → isFinished
自定義的operation必須加上狀態(tài)機(jī),否則加入到queue中無法運(yùn)行。
就像queue中的依賴,A 依賴 B,只有當(dāng)B中的狀態(tài)機(jī)的狀態(tài)為isFinished時才會開始執(zhí)行A。
該框架中的源碼如下,
typedef NS_ENUM(NSInteger, AFOperationState) {
AFOperationPausedState = -1,
AFOperationReadyState = 1,
AFOperationExecutingState = 2,
AFOperationFinishedState = 3,
};
- (void)setState:(AFOperationState)state {
if (!AFStateTransitionIsValid(self.state, state, [self isCancelled])) {
return;
}
[self.lock lock];
NSString *oldStateKey = AFKeyPathFromOperationState(self.state);
NSString *newStateKey = AFKeyPathFromOperationState(state);
[self willChangeValueForKey:newStateKey];
[self willChangeValueForKey:oldStateKey];
_state = state;
[self didChangeValueForKey:oldStateKey];
[self didChangeValueForKey:newStateKey];
[self.lock unlock];
}
當(dāng)收到服務(wù)器中的數(shù)據(jù)時候,會調(diào)用connection的代理方法:
- (void)connection:(NSURLConnection __unused *)connection
didReceiveData:(NSData *)data;
將受到的數(shù)據(jù)寫進(jìn)內(nèi)存中,收完之后,調(diào)用
- (void)connectionDidFinishLoading:(NSURLConnection __unused *)connection;
將內(nèi)存中的數(shù)據(jù)取出來,關(guān)閉connection,該operation狀態(tài)機(jī)狀態(tài)處于isFinished,并且調(diào)用該operation的completionBlock,對收到的數(shù)據(jù)進(jìn)行操作。
該框架對block的操作經(jīng)常看到這樣類型的:
__weak __typeof(self)weakSelf = self;
[super setCompletionBlock:^ {
__strong __typeof(weakSelf)strongSelf = weakSelf;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
dispatch_group_t group = strongSelf.completionGroup ?: url_request_operation_completion_group();
dispatch_queue_t queue = strongSelf.completionQueue ?: dispatch_get_main_queue();
#pragma clang diagnostic pop
dispatch_group_async(group, queue, ^{
block();
});
dispatch_group_notify(group, url_request_operation_completion_queue(), ^{
[strongSelf setCompletionBlock:nil];
});
}];
}
創(chuàng)建一個weakself ,防止block和self之間進(jìn)行循環(huán)引用,造成無法釋放。但之后有創(chuàng)建一個strongself防止self指向的對象提前釋放(block的執(zhí)行是異步的,可能block還沒有執(zhí)行完,operation就dealloc了,block內(nèi)又用到了,作者忽略了循環(huán)引用,因?yàn)樽詈筮@又設(shè)置這個block為nil,最終不會造成循環(huán)引用)。又因?yàn)閟trongSelf 之后又自動釋放了,不會造成循環(huán)引用;