基本內(nèi)容
本節(jié)主要介紹iOS中多線程相關(guān)的內(nèi)容麻裳,先簡單介紹NSOperation的使用骂租,然后結(jié)合GCD實現(xiàn)任務之間的管理洋访,比如每個任務完成之后的處理以及任務與任務之間的依賴镣陕,所有任務完成之后的處理等,通過AFNetworking源碼分析其具體實現(xiàn)姻政。
NSoperation
通過NSOpeartion去完成某個任務的時候呆抑,有3種方式:
- NSInvocationOperation
- NSBlockOperation
- 自己繼承NSOperation實現(xiàn)具體的任務操作
NSInvocationOperation
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSOperationQueue *queue = [NSOperationQueue new];
NSInvocationOperation *ope = [[NSInvocationOperation alloc] initWithTarget:
self selector:@selector(invocationOperationCall1) object:nil];
NSInvocationOperation *ope2 = [[NSInvocationOperation alloc] initWithTarget
:self selector:@selector(invocationOperationCall2) object:nil];
[queue addOperation:ope];
[queue addOperation:ope2];
}
-(void)invocationOperationCall1{
NSLog(@"%@", NSStringFromSelector(_cmd));
NSLog(@"%@", [NSThread currentThread]);
}
-(void)invocationOperationCall2{
NSLog(@"%@", NSStringFromSelector(_cmd));
NSLog(@"%@", [NSThread currentThread]);
}
輸出:
2016-01-13 14:23:54.791 operation+dispatch_group[6032:2162368]
invocationOperationCall1------<NSThread: 0x7fb6ad8019a0>{number = 3, name = (
null)}
2016-01-13 14:23:54.791 operation+dispatch_group[6032:2162371]
invocationOperationCall2------<NSThread: 0x7fb6adaec8f0>{number = 2, name = (
null)}
從日志中看出任務分別在不同的線程中執(zhí)行
NSInvocationOperation *ope = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationOperationCall1) object:nil];
NSInvocationOperation *ope2 = [[NSInvocationOperation alloc] initWithTarget:
self selector:@selector(invocationOperationCall2) object:nil];
[ope start];
[ope2 start];
輸出:
2016-01-13 14:27:38.226 operation+dispatch_group[6053:2164444]
invocationOperationCall1------<NSThread: 0x7fae69508370>{number = 1, name =
main}
2016-01-13 14:27:38.226 operation+dispatch_group[6053:2164444]
invocationOperationCall2------<NSThread: 0x7fae69508370>{number = 1, name =
main}
從日志中看出,如果不添加到queue中汁展,則任務是同步在當前線程中執(zhí)行的鹊碍。
還可以添加NSOperation對象之間的依賴厌殉,讓每個任務之間按一定的順序執(zhí)行
NSOperationQueue *queue = [NSOperationQueue new];
NSInvocationOperation *ope = [[NSInvocationOperation alloc] initWithTarget:self
selector:@selector(invocationOperationCall1) object:nil];
NSInvocationOperation *ope2 = [[NSInvocationOperation alloc] initWithTarget:
self selector:@selector(invocationOperationCall2) object:nil];
[ope addDependency:ope2];
[queue addOperation:ope];
[queue addOperation:ope2];
輸出:
2016-01-13 19:27:49.182 operation+dispatch_group[13865:2284382]
invocationOperationCall2------<NSThread: 0x7fa26300ff40>{number = 2, name = (
null)}
2016-01-13 19:27:49.183 operation+dispatch_group[13865:2284382]
invocationOperationCall1------<NSThread: 0x7fa26300ff40>{number = 2, name = (
null)}
雖然ope和ope2之間是并行的,但是添加依賴之后ope會等到ope2執(zhí)行完畢之后才開始執(zhí)行侈咕。
NSBlockOperation
NSBlockOperation *ope = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"task A, %@", [NSThread currentThread]);
}];
[ope addExecutionBlock:^{
NSLog(@"task B, %@", [NSThread currentThread]);
}];
[ope addExecutionBlock:^{
NSLog(@"task C, %@", [NSThread currentThread]);
}];
[ope start];
輸出:
2016-01-13 14:17:36.594 operation+dispatch_group[5990:2158464]
task A, <NSThread: 0x7ff1217073d0>{number = 1, name = main}
2016-01-13 14:17:36.594 operation+dispatch_group[5990:2158531]
task B, <NSThread: 0x7ff1215029f0>{number = 2, name = (null)}
2016-01-13 14:17:36.594 operation+dispatch_group[5990:2158532]
task C, <NSThread: 0x7ff1217a6af0>{number = 3, name = (null)}
自定義NSOperation(結(jié)合AFNetworking源碼)
AFNetworking源代碼:
AFURLConnectionOperation.m
@interface AFURLConnectionOperation : NSOperation <NSURLConnectionDelegate,
NSURLConnectionDataDelegate, NSSecureCoding, NSCopying>
AFURLConnectionOperation是繼承自NSOperation
AFURLConnectionOperation.m
- (void)start {
[self.lock lock];
if ([self isCancelled]) {
[self performSelector:@selector(cancelConnection) onThread:[[self class
] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.
runLoopModes allObjects]];
} else if ([self isReady]) {
self.state = AFOperationExecutingState;
//使用AFNetworking網(wǎng)絡線程去調(diào)用OperationDidStart函數(shù)
[self performSelector:@selector(operationDidStart) onThread:[[self
class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[
self.runLoopModes allObjects]];
}
[self.lock unlock];
}
//類方法 獲取網(wǎ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];
[_networkRequestThread start];
});
return _networkRequestThread;
}
//初始化網(wǎng)絡線程
+ (void)networkRequestThreadEntryPoint:(id)__unused object {
@autoreleasepool {
[[NSThread currentThread] setName:@"AFNetworking"];
//新建網(wǎng)絡線程的runloop對象
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
//保證網(wǎng)絡線程在沒有任何port消息的時候一直處于活躍狀態(tài)公罕,防止進入休眠,隨時處理網(wǎng)絡請求或者數(shù)據(jù)返回耀销。
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
//開始運行網(wǎng)絡線程的runloop
[runLoop run];
}
}
- (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) {
//將網(wǎng)絡請求相關(guān)的事件源添加到網(wǎng)絡線程的runloop中楼眷,網(wǎng)絡回調(diào)和數(shù)據(jù)返回都依賴于網(wǎng)絡線程runloop去監(jiān)聽
[self.connection scheduleInRunLoop:runLoop forMode:runLoopMode];
//將流相關(guān)的事件源添加到網(wǎng)絡線程的runloop中,流的讀寫都依賴于網(wǎng)絡線程runloop去監(jiān)聽
[self.outputStream scheduleInRunLoop:runLoop forMode:runLoopMode];
}
[self.outputStream open];
//開始網(wǎng)絡請求
[self.connection start];
}
[self.lock unlock];
//讓主線程去觸發(fā)”已經(jīng)開始網(wǎng)絡請求“這一事件树姨。
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:
AFNetworkingOperationDidStartNotification object:self];
});
}
AFURLConnectionOperation自己重寫了NSOperation中的start方法摩桶,具體指定了需要執(zhí)行的任務。多個請求同時進行的時候帽揪,對網(wǎng)絡數(shù)據(jù)回調(diào)的處理都是由AFNetworking的網(wǎng)絡線程進行的硝清。
多個線程任務之間的管理
有時候,可能需要在所有網(wǎng)絡請求完成之后需要做一件事情或者是不同的網(wǎng)絡請求直接還有一定的依賴關(guān)系或者是順序關(guān)系转晰,這種情況下需要使用GCD的group去處理芦拿。
GCD group簡單介紹:
通過group可以對多個task進行統(tǒng)一管理,比如現(xiàn)在需要在多個任務完成之后再去做最后的處理:
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("com.ConcurrentQueue",
DISPATCH_QUEUE_CONCURRENT);
dispatch_group_async(group, queue, ^{
NSLog(@"task A");
});
dispatch_group_async(group, queue, ^{
NSLog(@"task B");
});
dispatch_group_notify(group, queue, ^{
NSLog(@"task LAST");
});
dispatch_group_async(group, queue, ^{
NSLog(@"task C");
});
輸出
2016-01-13 19:18:13.814 operation+dispatch_group[13783:2280382] task B
2016-01-13 19:18:13.814 operation+dispatch_group[13783:2280385] task C
2016-01-13 19:18:13.814 operation+dispatch_group[13783:2280383] task A
2016-01-13 19:18:13.815 operation+dispatch_group[13783:2280383] task LAST
通過group讓task LAST等到ABC任務執(zhí)行完之后最后進行處理查邢。
AFNetworking中多任務之間的管理實現(xiàn)
+ (NSArray *)batchOfRequestOperations:(NSArray *)operations
progressBlock:(void (^)(NSUInteger
numberOfFinishedOperations, NSUInteger
totalNumberOfOperations))progressBlock
completionBlock:(void (^)(NSArray *operations))
completionBlock
{
if (!operations || [operations count] == 0) {
return @[[NSBlockOperation blockOperationWithBlock:^{
dispatch_async(dispatch_get_main_queue(), ^{
if (completionBlock) {
completionBlock(@[]);
}
});
}]];
}
__block dispatch_group_t group = dispatch_group_create();
//等待所有任務都完成之后最后需要處理的任務
NSBlockOperation *batchedOperation = [NSBlockOperation
blockOperationWithBlock:^{
//通過dispatch_group_notify保證completionBlock當前group
//中的主線程隊列上所有的originalCompletionBlock任務處理完之后再處理
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
if (completionBlock) {
completionBlock(operations);
}
});
}];
for (AFURLConnectionOperation *operation in operations) {
operation.completionGroup = group;
void (^originalCompletionBlock)(void) = [operation.completionBlock copy];
__weak __typeof(operation)weakOperation = operation;
//改變每個Operation的completionBlock蔗崎,主要是添加了能顯示任務完成進度的功能。
operation.completionBlock = ^{
__strong __typeof(weakOperation)strongOperation = weakOperation;
//忽略行級別的編譯器警告
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
dispatch_queue_t queue = strongOperation.completionQueue ?: dispatch_get_main_queue();
#pragma clang diagnostic pop
dispatch_group_async(group, queue, ^{
//originalCompletionBlock任務可能會被放到dispatch_main_queue中處理扰藕。
if (originalCompletionBlock) {
originalCompletionBlock();
}
//過濾出已經(jīng)完成的任務
NSUInteger numberOfFinishedOperations = [[operations
indexesOfObjectsPassingTest:^BOOL(id op, NSUInteger __unused
idx, BOOL __unused *stop) {
return [op isFinished];
}] count];
if (progressBlock) {
//顯示任務完成的進度
progressBlock(numberOfFinishedOperations, [operations count
]);
}
dispatch_group_leave(group);
});
};
dispatch_group_enter(group);
//添加batchedOperation對所有Operation任務的依賴缓苛,保證batchedOperation最后處理。
[batchedOperation addDependency:operation];
}
return [operations arrayByAddingObject:batchedOperation];
}
這個函數(shù)是將多個任務進行打包處理邓深,并能顯示出完成進度未桥,以及最后等待所有任務完成之后進行最后的處理。