iOS底层探索 ----- GCD基础&源码分析 上

1,621 阅读7分钟

前言

上篇文章对多线程的基础做了一个详细的讲解,那么这篇就接着往下继续探究。

资源准备

pthread

pthreadPOSIX 线程的简称,是线程的 POSIX 标准。pthread 基于 C 语言,是一种可以跨平台使用的方法,但是由于其使用难度较大,并且生命周期需要程序员手动管理,因此很少或几乎不用。下面举个例子:

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ 
    // 1: pthread
    pthread_t threadId = NULL; 
    // c字符串 
    char *cString = "HelloCode"; 
    int result = pthread_create(&threadId, NULL, pthreadTest, cString);
    if (result == 0) {
        NSLog(@"成功"); 
    } else {
        NSLog(@"失败"); 
    } 
}

void *pthreadTest(void *para){ 
    // 接 C 语言的字符串 
    // NSLog(@"===> %@ %s", [NSThread currentThread], para); 
    // __bridge 将 C 语言的类型桥接到 OC 的类型 
    NSString *name = (__bridge NSString *)(para); 
    NSLog(@"===>%@ %@", [NSThread currentThread], name); 
    return NULL; 
}

整个思路如下:

使用pthread_create创建线程;

接着就是参数设置:

  • pthread_t:要创建线程的结构体指针,通常开发的时候,如果遇到 C 语言的结构体,类型后缀 _t / Ref 结尾,同时不需要 *

  • 线程的属性,nil(空对象,OC使用的) ,NULL(空地址,C使用的);

  • 线程要执行的函数地址:

    • void *:返回类型,表示指向任意对象的指针,和OC中的id类似;
    • (*):函数名;
    • (void *):参数类型,void *
  • 传递给第三个参数(函数)的参数。

获取返回值(C 语言框架中非常常见):

  • int类型:

    • 0:创建线程成功;
    • 0:创建线程失败的错误码,失败有多种可能。

COC的桥接

  • __bridge只做类型转换,但是不修改对象(内存)管理权;

  • __bridge_retained(也可以使用CFBridgingRetain)将Objective-C的对象转换为Core Foundation的对象,同时将对象(内存)的管理权交给我们,后续需要使用CFRelease或者相关方法来释放对象;

  • __bridge_transfer(也可以使用CFBridgingRelease)将Core Foundation的对象转换为Objective-C的对象,同时将对象(内存)的管理权交给ARC

NSThread

NSthread是苹果官方提供面向对象的线程操作技术,是对thread的上层封装,比较偏向于底层。简单方便,可以直接操作线程对象,使用频率较少。

创建线程的三种方式:

  • 通过init初始化方式创建,开辟线程,启动线程;

  • detach分离,不需要启动,直接分离出新的线程执行;

  • 隐式的多线程调用方法,没有thread,也没有start。所属NSObjectNSThreadPerformAdditions分类。

举个创建例子:

- (void)creatThreadMethod{

    NSLog(@"%@", [NSThread currentThread]);

    //方式一: 1:开辟线程
    NSThread *t = [[NSThread alloc] initWithTarget:self.p selector:@selector(study:) object:@100];
    // 2. 启动线程
    [t start];
    t.name = @"学习线程";

    //方式二: detach 分离,不需要启动,直接分离出新的线程执行
    [NSThread detachNewThreadSelector:@selector(study:) toTarget:self.p withObject:@10000];

    //NSObject (NSThreadPerformAdditions)的分类

    //方式三 : `隐式`的多线程调用方法,没有thread,也没有 start
    [self.p performSelectorInBackground:@selector(study:) withObject:@5000];
    
    NSLog(@"%@", [NSThread currentThread]);
}

属性

可以在 Foundation 库里面,看 NSThread 的对外属性API,如下图: 7910F007-AE90-45FD-BFAE-A78C74539847.png

我们逐个分析下:

  • name:在应用程序中,收集错误日志,能够记录工作的线程。否则不好判断具体哪一个线程出的问题;

  • stackSize:该值必须以字节为单位,且为4KB的倍数;

  • isExecuting:线程是否在执行;

  • isCancelled:线程是否被取消;

  • isFinished:是否完成;

  • isMainThread:是否是主线程;

  • threadPriority:线程的优先级,取值范围0.0-1.0,默认优先级0.51.0表示最高优先级,优先级高CPU调度的频率高;

  • qualityOfService:服务质量,替代threadPriority属性。

- (void)testThreadProperty{
    // 主线程 512K 
    NSLog(@"NSThread:%@,stackSize:%zdK,isMainThread:%d", [NSThread currentThread], [NSThread currentThread].stackSize / 1024, [NSThread currentThread].isMainThread); 
    
    NSThread *t = [[NSThread alloc] initWithTarget:self selector:@selector(eat) object:nil]; 
    t.name = @"吃饭线程"; 
    t.stackSize = 1024*1024; 
    t.threadPriority = 1; 
    [t start]; 
}

看下打印结果: DE79FF17-1477-47A5-9EC3-052C4742048B.png

类方法

结合上面的属性api 的图片,再对相应 api 进行分析:

  • currentThread:获取当前线程;

  • sleepForTimeInterval:阻塞线程;

  • exit:退出线程;

  • mainThread:获取主线程;

- (void)threadTest { 
    //当前线程 
    [NSThread currentThread]; 
    // 如果返回值为1,表示主线程,否则是子线程 
    NSLog(@"%@", [NSThread currentThread]); 
    
    //阻塞休眠 
    [NSThread sleepForTimeInterval:2];//休眠时长,单位秒 
    [NSThread sleepUntilDate:[NSDate date]];//休眠到指定时间 
    
    //退出线程 
    [NSThread exit];
    //判断当前线程是否为主线程 
    [NSThread isMainThread]; 
    //判断当前线程是否是多线程 
    [NSThread isMultiThreaded]; 
    //主线程的对象 
    NSThread *mainThread = [NSThread mainThread]; 
    NSLog(@"%@", mainThread); 
}

exit & cancel的区别

  • exit:一旦强行终止线程,后续的所有代码都不会执行;

  • cancel:取消当前线程,但是不能取消正在执行的线程;

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ 
    [self testThreadStatus]; 
}

- (void)testThreadStatus{ 
    NSLog(@"%d %d %d", self.t.isExecuting, self.t.isFinished, self.t.isCancelled); 
    if ( self.t == nil || self.t.isCancelled || self.t.isFinished ) { 
        self.t = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil]; 
        self.t.name = @"跑步线程"; 
        [self.t start]; 
    } else { 
        NSLog(@"%@ 正在执行",self.t.name); 
        [self.t cancel]; 
        self.t = nil; 
    } 
}

- (void)run{ 

    NSLog(@"开始"); 
    
    // 下面的代码的作用是判断线程状态,可能因为下面的延时,阻塞会带来当前线程的一些影响 
    [NSThread sleepForTimeInterval:3];
    
    // 判断线程是否被取消 
    if ([NSThread currentThread].isCancelled) {
         NSLog(@"%@被取消",self.t.name);
         return; 
     }
     
     for (NSInteger i = 0; i < 10; i++) { 
     
         // 判断线程是否被取消 
         if ([NSThread currentThread].isCancelled) { 
             NSLog(@"%@被取消",self.t.name); 
             return; 
         } 
     
         if (i == 3) { 
             // 睡指定的时长(秒) 
             [NSThread sleepForTimeInterval:1]; 
          } 
         
         NSLog(@"%@ %zd", [NSThread currentThread], i); 
         
         // 内部取消线程 
         // 强制退出 - 当某一个条件满足,不希望线程继续工作,直接杀死线程,退出 
         if (i == 8) { 
             // 强制退出当前所在线程!后续的所有代码都不会执行 
             [NSThread exit]; 
         }
     }
     
     NSLog(@"完成"); 
}

GCD

dispatch_after

dispatch_after:表示在某队列中的block延迟执行:

  • 应用场景:在主队列上延迟执行一项任务,如viewDidload之后延迟1s,提示一个alertview(是延迟加入到队列,而不是延迟执行)
- (void)testGCD { 
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 
        NSLog(@"2s后输出");
    }); 
}

dispatch_once

dispatch_once:保证在App运行期间,block中的代码只执行一次

  • 应用场景:单例模式、Method Swizzling
- (void)testGCD { 
    static dispatch_once_t onceToken; 
    dispatch_once(&onceToken, ^{ 
        ... 
    }); 
}

dispatch_apply

dispatch_apply:将指定的Block追加到指定的队列中重复执行,并等到全部的处理执行结束,相当于线程安全的循环:

  • 应用场景:用来拉取网络数据后提前算出各个控件的大小,防止绘制时计算,提高表单滑动流畅性:
    • 添加到串行队列中:按序执行;
    • 添加到主队列中:死锁;
    • 添加到并发队列中:乱序执行;
    • 添加到全局队列中:乱序执行;
- (void)testGCD{ 
    dispatch_queue_t queue = dispatch_queue_create("testGCD", DISPATCH_QUEUE_SERIAL);
    NSLog(@"dispatch_apply前"); 
    dispatch_apply(10, queue, ^(size_t index) { 
        NSLog(@"dispatch_apply 的线程 %zu - %@", index, [NSThread currentThread]);
    }); 
    
    NSLog(@"dispatch_apply后"); 
}

dispatch_apply 中的参数:

  • 参数1 -- 10 :重复次数, 设置的10次;
  • 参数2 -- queue :添加的队列;
  • 参数3 -- 一个 block:任务Block

dispatch_group_t

dispatch_group_t:调度组将任务分组执行,能监听任务组完成,并设置等待时间

  • 应用场景:多个接口请求之后刷新页面;

  • dispatch_group_create:创建一个调度任务组;

  • dispatch_group_async:把一个任务异步提交到任务组里;

- (void)testGCD { 
    dispatch_group_t group = dispatch_group_create(); 
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0); 
    
    dispatch_group_async(group, queue, ^{ 
        NSLog(@"接口1"); 
    });
    
    dispatch_group_async(group, queue, ^{ 
        NSLog(@"接口2"); 
    }); 
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{ 
        NSLog(@"刷新"); 
    }); 
}

dispatch_group_enter & dispatch_group_leave

dispatch_group_enterdispatch_group_leave成对出现,使进出组的逻辑更加清晰

  • 这种方式用在不使用dispatch_group_async来提交任务,且必须配合使用
- (void)testGCD { 
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0); 
    
    dispatch_group_enter(group); 
    dispatch_async(queue, ^{     
        NSLog(@"接口1"); 
        dispatch_group_leave(group); 
    });
    
    dispatch_group_enter(group); 
    dispatch_async(queue, ^{
        NSLog(@"接口2");
        dispatch_group_leave(group); 
    }); 
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"刷新"); 
    }); 
}

dispatch_group_wait

dispatch_group_wait:设置等待时间

  • 在等待时间结束后,如果还没有执行完任务组,则返回。返回0代表执行成功,非0则执行失败; api 的对应参数

  • group:需要等待的调度组;

  • timeout:等待的超时时间(即等多久);

    • DISPATCH_TIME_NOW:不等待,直接判定调度组是否执行完毕;

    • DISPATCH_TIME_FOREVER:阻塞当前调度组,直到调度组执行完毕

- (void)testGCD {
    dispatch_group_t group = dispatch_group_create(); 
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0); 
    
    dispatch_group_enter(group); dispatch_async(queue, ^{ 
        NSLog(@"接口1"); 
        dispatch_group_leave(group); 
    });
    
    dispatch_group_enter(group); 
    dispatch_async(queue, ^{ 
        NSLog(@"接口2");
        dispatch_group_leave(group); 
    }); 
    
    // long timeout = dispatch_group_wait(group, DISPATCH_TIME_NOW); 
    // long timeout = dispatch_group_wait(group, DISPATCH_TIME_FOREVER); 
    
    long timeout = dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 30 * NSEC_PER_SEC)); 
    
    if (timeout == 0) {
        NSLog(@"完成"); 
    } else { 
        NSLog(@"超时"); 
    } 
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"刷新");
    }); 
}

返回值:

  • long类型:

    • 0:在指定时间内调度组完成了任务;

    • 0:在指定时间内调度组没有按时完成任务。

dispatch_barrier_async

dispatch_barrier_async:先执行栅栏前任务,再执行栅栏任务,最后执行栅栏后任务

  • 应用场景:无需等待栅栏执行完,会继续往下走
- (void)testGCD {

    dispatch_queue_t queue = dispatch_queue_create("testGCD", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"逻辑1 - %@", [NSThread currentThread]);
    
    dispatch_async(queue, ^{
        sleep(2);
        NSLog(@"接口1 - %@", [NSThread currentThread]);
    });
    NSLog(@"逻辑2 - %@", [NSThread currentThread]);
    
    dispatch_barrier_async(queue, ^{
        NSLog(@"栅栏 - %@", [NSThread currentThread]);
    });
    
    NSLog(@"逻辑3 - %@", [NSThread currentThread]);
    
    dispatch_async(queue, ^{
        sleep(2);
        NSLog(@"接口2 - %@", [NSThread currentThread]);
    });
    
    NSLog(@"逻辑4 - %@", [NSThread currentThread]);}

打印结果:

//输出以下内容: 
逻辑1 - {number = 1, name = main} 
逻辑2 - {number = 1, name = main} 
逻辑3 - {number = 1, name = main} 
逻辑4 - {number = 1, name = main} 
接口1 - {number = 8, name = (null)} 
栅栏 -  {number = 8, name = (null)} 
接口2 - {number = 8, name = (null)}

dispatch_barrier_sync

dispatch_barrier_sync:和dispatch_barrier_async作用相同,但会堵塞线程,影响后面的任务执行:

  • 需要等待栅栏执行完,才会执行栅栏后面的任务,谨慎使用。
- (void)testGCD{
    dispatch_queue_t queue = dispatch_queue_create("testGCD", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"逻辑1 - %@", [NSThread currentThread]);
    
    dispatch_async(queue, ^{
        sleep(2);
        NSLog(@"接口1 - %@", [NSThread currentThread]);
    });
    
    NSLog(@"逻辑2 - %@", [NSThread currentThread]);
        
    dispatch_barrier_async(queue, ^{
        NSLog(@"栅栏 - %@", [NSThread currentThread]);
    });
    
    NSLog(@"逻辑3 - %@", [NSThread currentThread]);
    
    dispatch_async(queue, ^{
        sleep(2);
        NSLog(@"接口2 - %@", [NSThread currentThread]);
    });
    
    NSLog(@"逻辑4 - %@", [NSThread currentThread]);}

输入内容:

//输出以下内容: 
逻辑1 - {number = 1, name = main} 
逻辑2 - {number = 1, name = main} 
逻辑3 - {number = 1, name = main} 
逻辑4 - {number = 1, name = main} 
接口1 - {number = 8, name = (null)} 
栅栏 -  {number = 8, name = (null)} 
接口2 - {number = 8, name = (null)}

dispatch_semaphore_t

dispatch_semaphore_t:信号量

  • 应用场景:用作同步锁,用于控制GCD最大并发数;
  • dispatch_semaphore_create:创建信号量;
  • dispatch_semaphore_wait:等待信号量,信号量减1。当信号量< 0时会阻塞当前线程,根据传入的等待时间决定接下来的操作。如果永久等待将等到信号signal才执行下去;
  • dispatch_semaphore_signal:释放信号量,信号量加1。当信号量>= 0会执行wait之后的代码。
- (void)testGCD { 

    dispatch_queue_t queue = dispatch_queue_create("testGCD", DISPATCH_QUEUE_CONCURRENT); 
    
    for (int i = 0; i < 10; i++) { 
        dispatch_async(queue, ^{ 
            sleep(1); 
            NSLog(@"当前 - %d, 线程 - %@", i, [NSThread currentThread]); 
        }); 
    } 
    
    dispatch_semaphore_t sem = dispatch_semaphore_create(0);
    
    for (int i = 0; i < 10; i++) { 
        dispatch_async(queue, ^{ 
            sleep(1);
            NSLog(@"当前 - %d, 线程 - %@", i, [NSThread currentThread]); 
            dispatch_semaphore_signal(sem);
        }); 
        
        dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); 
    } 
    
    NSLog(@"完成");
}

dispatch_source_t

iOS中一般使用NSTimer来处理定时逻辑,但NSTimer是依赖Runloop的,而Runloop可以运行在不同的模式下。如果NSTimer添加在一种模式下,当Runloop运行在其他模式下的时候,定时器会处于挂起状态。如果Runloop在阻塞状态,NSTimer触发时间就会推迟到下一个Runloop周期。因此NSTimer在计时上会有误差,并不是特别精确,而GCD定时器不依赖Runloop,计时精度要高很多。

dispatch_source_t:用于计时操作,它创建的timer不依赖于RunLoop,计时精准度比NSTimer高:

  • 应用场景:GCDTimer
static dispatch_source_t timer; 

- (void)testGCD { 
    //1.创建队列 
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0); 
    
    //2.创建timer 
    timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); 
    
    //3.设置timer首次执行时间,间隔,精确度 
    dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC, 0.1 * NSEC_PER_SEC);
    
    //4.设置timer事件回调 
    dispatch_source_set_event_handler(timer, ^{ 
        NSLog(@"计时"); 
    });
    
    //5.默认是挂起状态,需要手动激活
    dispatch_resume(timer); 
}

dispatch_source:一种基本的数据类型,可以用来监听一些底层的系统事件

  • Timer Dispatch Source:定时器事件源,用来生成周期性的通知或回调

  • Signal Dispatch Source:监听信号事件源,当有UNIX信号发生时会通知

  • Descriptor Dispatch Source:监听文件或socket事件源,当文件或socket数据发生变化时会通知
  • Process Dispatch Source:监听进程事件源,与进程相关的事件通知
  • Mach port Dispatch Source:监听Mach端口事件源
  • Custom Dispatch Source:监听自定义事件源

常用API

  • dispatch_source_create: 创建事件源
  • dispatch_source_set_event_handler: 设置数据源回调
  • dispatch_source_merge_data: 设置事件源数据
  • dispatch_source_get_data: 获取事件源数据
  • dispatch_resume: 继续
  • dispatch_suspend: 挂起
  • dispatch_cancle: 取消

NSOperation

NSOperation是基于GCD之上的更高一层封装,NSOperation需要配合NSOperationQueue来实现多线程。

实现多线程的步骤:

  • 创建任务:先将需要执行的操作封装到NSOperation对象中;

  • 创建队列:创建NSOperationQueue

  • 将任务加入到队列中:将NSOperation对象添加到NSOperationQueue中;

- (void)testNSOperation { 
    //处理事务 
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(handleInvocation:) object:@"testNSOperation"]; 
    
    //创建队列 
    NSOperationQueue *queue = [[NSOperationQueue alloc] init]; 
    
    //操作加入队列 
    [queue addOperation:op]; 
} 

- (void)handleInvocation:(id)operation{ 
    NSLog(@"%@ - %@", operation, [NSThread currentThread]); 
}

NSOperation是抽象类,开发中实际用到的是它的子类:

  • NSInvocationOperation
  • NSBlockOperation
  • 自定义子类

下面详细分析。

NSInvocationOperation

- (void)testNSOperation {
    NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(handleInvocation:) object:@"testNSOperation"]; 
    [invocationOperation start]; 
}

NSBlockOperation

  • 通过addExecutionBlock方法,可以让NSBlockOperation实现多线程;

  • NSBlockOperation创建block中的任务是在主线程执行,而使用addExecutionBlock加入的任务是在子线程执行;

- (void)testNSOperation { 
    NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{ 
        NSLog(@"main task:%@", [NSThread currentThread]); 
    }]; 
    
    [blockOperation addExecutionBlock:^{ 
        NSLog(@"task1:%@", [NSThread currentThread]); 
    }]; 
    
    [blockOperation addExecutionBlock:^{ 
        NSLog(@"task2:%@", [NSThread currentThread]); 
    }]; 
    
    [blockOperation addExecutionBlock:^{ 
        NSLog(@"task3:%@", [NSThread currentThread]); 
    }]; 
    
    [blockOperation start]; 
}

自定义子类

  • 使用NSOperation的子类,重写它的main方法;
@interface LGOperation : NSOperation 

@end 

@implementation LGOperation 

- (void)main { 
    for (int i = 0; i < 3; i++) {
        NSLog(@"NSOperation的子类:%@",[NSThread currentThread]); 
    } 
}
@end 

- (void)testNSOperation{ 
    LGOperation *operation = [[LGOperation alloc] init]; 
    [operation start]; 
}

NSOperationQueue

NSOperationQueue包含两种队列:

  • 主队列:主队列上的任务是在主线程执行的;

  • 其他队列:包含串行和并发队列,加入到非主队列中的任务,默认就是并发,开启多线程.

- (void)testNSOperation { 
    // 初始化添加事务
    NSBlockOperation *bo = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"任务1:%@",[NSThread currentThread]);
    }]; 
    
    // 添加事务 
    [bo addExecutionBlock:^{ 
        NSLog(@"任务2:%@",[NSThread currentThread]); 
    }]; 
    
    // 回调监听 
    bo.completionBlock = ^{
        NSLog(@"完成"); 
    }; 
    
    NSOperationQueue *queue = [[NSOperationQueue alloc] init]; 
    [queue addOperation:bo]; 
    NSLog(@"事务添加进了NSOperationQueue"); 
}
  • NSInvocationOperationNSBlockOperation的区别:

    • NSInvocationOperation类似于target形式;

    • NSBlockOperation类似于block形式。函数式编程,业务逻辑代码可读性更高.

执行顺序

- (void)testNSOperation { 
    NSOperationQueue *queue = [[NSOperationQueue alloc] init]; 
    
    for (int i = 0; i < 5; i++) { 
        [queue addOperationWithBlock:^{ 
            NSLog(@"%@---%d", [NSThread currentThread], i); 
        }]; 
    } 
}

优先级

- (void)testNSOperation {
    NSBlockOperation *bo1 = [NSBlockOperation blockOperationWithBlock:^{ 
        for (int i = 0; i < 5; i++) { 
            NSLog(@"第一个操作 %d --- %@", i, [NSThread currentThread]); 
        }
    }]; 
    
    // 设置最高优先级 
    bo1.qualityOfService = NSQualityOfServiceUserInteractive; 
    
    NSBlockOperation *bo2 = [NSBlockOperation blockOperationWithBlock:^{ 
        for (int i = 0; i < 5; i++) { 
            NSLog(@"第二个操作 %d --- %@", i, [NSThread currentThread]); 
        } 
    }]; 
    
    // 设置最低优先级 
    bo2.qualityOfService = NSQualityOfServiceBackground;
    
    NSOperationQueue *queue = [[NSOperationQueue alloc] init]; 
    [queue addOperation:bo1]; 
    [queue addOperation:bo2]; 
}

并发数

  • GCD中只能使用信号量来设置并发数,而NSOperation使用maxConcurrentOperationCount就能设置并发数;
- (void)testNSOperation {
    NSOperationQueue *queue = [[NSOperationQueue alloc] init]; 
    
    queue.name = @"Felix"; 
    queue.maxConcurrentOperationCount = 2;
    
    for (int i = 0; i < 5; i++) { 
        [queue addOperationWithBlock:^{ 
            [NSThread sleepForTimeInterval:2]; 
            NSLog(@"%d-%@",i,[NSThread currentThread]); 
        }]; 
    } 
}

添加依赖

- (void)testNSOperation { 
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    NSBlockOperation *bo1 = [NSBlockOperation blockOperationWithBlock:^{ 
        [NSThread sleepForTimeInterval:0.5];
        NSLog(@"获取用户id"); 
    }];
    
    NSBlockOperation *bo2 = [NSBlockOperation blockOperationWithBlock:^{ 
        [NSThread sleepForTimeInterval:0.5];
        NSLog(@"通过用户id,获取用户对象"); 
    }]; 
    
    NSBlockOperation *bo3 = [NSBlockOperation blockOperationWithBlock:^{ 
        [NSThread sleepForTimeInterval:0.5];
        NSLog(@"通过用户对象,获取文章列表");
    }]; 
    
    [bo2 addDependency:bo1]; 
    [bo3 addDependency:bo2]; 
    
    [queue addOperations:@[bo1,bo2,bo3] waitUntilFinished:YES]; 
    
    NSLog(@"完成");
}

线程间通讯

- (void)testNSOperation { 
    NSOperationQueue *queue = [[NSOperationQueue alloc] init]; 
    queue.name = @"test";
    
    [queue addOperationWithBlock:^{ 
        NSLog(@"接口请求:%@--%@", [NSOperationQueue currentQueue], [NSThread currentThread]); 
        
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{ 
            NSLog(@"刷新:%@--%@", [NSOperationQueue currentQueue], [NSThread currentThread]); 
        }]; 
    }];
}