iOS-多线程(NSThread+GCD+NSOperation)

一、基本概念

  1. 进程:系统中正在运行的应用程序。每个程序都是相互独立的,并且运行在一块独有的受保护的内存中
  2. 线程:进程基本执行路径。一个进程想要执行任务,就必须通过线程来执行。单条的线程通过串行的方式执行任务,也就是说任务是一个一个按顺序的执行
  3. 多线程:开启多条线程,让任务并发执行。(原理:在同一时间内,CPU 只会处理一条线程,多线程并发执行其实是,cpu在不同的线程之间快速的切换处理,只要cpu切换的足够快,就会给人多线程并发执行任务的假象)
    补充:并行指的是任务一起执行,强调的是过程;并发指的是任务以一起执行的方式开始执行,强调的是任务开始执行的方式。

二、多线程的优缺点

优点
  1. 可提高程序的执行效率
  2. 可合理提高cpu和内存的利用率
缺点
  1. 开启一条线程所占用的内存较大(主线程 1M ;子线程 512KB)
  2. 线程多时,cpu在在切换线程上的开销较大,影响性能
  3. 线程间的通讯和数据共享等会将大程序设计的复杂度

三、多线程的实现方案

1. NSThread

此方案是经过苹果封装后完全面向对象的,可以直接拿到线程对象进行操作,很直观、便捷。但是缺点就是需手动管理线程生命周期,所以不经常使用
1.1 创建(3种方式)

//  1.手动启动线程,可拿到线程对象进行额外的设置
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(addThread:) object:@"hahaha"];
    [thread start];
    
    //  2.分离出子线程 - 自动启动线程
    [NSThread detachNewThreadSelector:@selector(addChildThread:) toTarget:self withObject:@"子线程"];
    
    //  3.后台线程 - 自动启动线程
    [self performSelectorInBackground:@selector(addBackgroundThread:) withObject:@"后台线程"];

1.2 NSThread 常涉及到的方法

    [thread cancel];  //取消线程
    [NSThread exit];
    [NSThread sleepForTimeInterval:3.0];  //阻塞线程
    [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:3.0]];
    [NSThread currentThread];  //获取当前线程
    [NSThread mainThread];  //获取主线程
    [NSThread isMultiThreaded];  //是否是多线程,返回BOOL值

1.3 线程间通信

- (void)viewDidLoad {
    [super viewDidLoad];

    //  创建子线程
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(addThread:) object:@"hahaha"];
    [thread start];
}

- (void)addThread:(NSString *)name {
    NSLog(@"%@",[NSThread currentThread]);
    
    //  在子线程中回到主线程的两种方式
    [self performSelectorOnMainThread:@selector(goBackMainThread:) withObject:@"main" waitUntilDone:YES];
    [self performSelector:@selector(goBackMainThread:) onThread:[NSThread mainThread] withObject:@"test" waitUntilDone:YES];
}

- (void)goBackMainThread:(NSString *)name {
    NSLog(@"%@",[NSThread currentThread]);
}

打印结果

2016-08-01 14:20:30.253 demo[66988:3541822] <NSThread: 0x79e71ab0>{number = 2, name = (null)}
2016-08-01 14:20:30.258 demo[66988:3541733] <NSThread: 0x79e72a30>{number = 1, name = main}
2016-08-01 14:20:30.259 demo[66988:3541733] <NSThread: 0x79e72a30>{number = 1, name = main}

1.4 线程安全
当多个线程访问同一块资源时会发生数据安全问题,也称为线程同步 ,这时候就需要加互斥锁 @synchronized(self){}

2. GCD

苹果为多核并行运算开发的解决方案,它可以自动的利用cpu的内核,并且系统自动管理线程的生命周期
开发者要做的是将任务添加到队列中,系统会自动将队列中的任务按FIFO的原则取出任务(FIFO:先进先出,后进后出,如果以并发方式执行任务,则先出的任务还没结束前下一个的任务可以出列)

2.1 任务和队列的理解
任务:决定能不能开启新线程,分同步和异步任务两种

-同步:任务只能在当前的线程执行,不具备开启新线程的能力(同步任务比较霸道,必须在当前的分配任务执行完,才算把分配任务的任务结束,和后面说的死锁有关联)
-异步:任务可以在其它线程中执行,具备有开启新线程的能力

    //  同步任务
    dispatch_sync(dispatch_get_global_queue(0, 0), ^{
    });
    //  异步任务
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
    });
队列:决定任务将以什么方式执行,分串行和并发队列两种

-串行:任务将一个接一个的执行(一个任务执行完才开始下一个任务)
-并发:多个任务可以并发(同时)执行,要在异步任务函数中才能有效

队列的创建

-串行:系统提供两种方式

    //  1.系统提供的主队列
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    //  2.创建新的串行队列(参数1:队列名字,apple推荐使用com.example.myQueue的规则来命名队列,用于debug的时候追踪队列以便于调试,可以为空;
    //    参数2:可以传人NULL或DISPATCH_QUEUE_SERIAL表示串行队列)
    dispatch_queue_t queue = dispatch_queue_create("com.hhh.eatShit", NULL);

- 并发:系统提供两种方式

    //  1.系统提供的全局队列(参数1:队列的优先级;参数2:传0即可)
    dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    //  2.创建新的并发队列
    dispatch_queue_t queue = dispatch_queue_create("com.hhh.eatShit", DISPATCH_QUEUE_CONCURRENT);
2.2 GCD使用方式(任务和队列的结合方式)
  1. 异步任务+并发队列:使用频率高,开启多线程并发的执行任务
  2. 异步任务+串行队列:使用频率高,开启一条新线程,串行执行任务
  3. 同步任务+并发队列:基本不用,不开线程在当前线程串行执行任务我
  4. 同步任务+串行队列:基本不用,不开线程在当前线程串行执行任务我
  5. 异步任务+主队列:不开线程,在主线程中串行执行任务我
  6. 同步任务+主队列:不开线程,在主线程中串行执行任务我(注意死锁)
    注意:在当前的串行队列中添加同步任务到当前队列会发生死锁
- (void)viewDidLoad {
    [super viewDidLoad];
    
    //  主队列是串行的,在主队里中往主队列添加同步任务,发生死锁,无法执行打印内容
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"快把我打印出来");
    });
}
2.3 🍐演示GCD的使用
  1. 异步任务+并发队列
- (void)viewDidLoad {
    [super viewDidLoad];

    //  由系统决定开几条线程
    dispatch_queue_t queue = dispatch_queue_create("com.demo.bingfa", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        NSLog(@"任务1---%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"任务2---%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"任务3---%@",[NSThread currentThread]);
    });
}

打印

2016-08-01 17:27:01.793 demo[79533:3667100] 任务1---<NSThread: 0x79858c40>{number = 2, name = (null)}
2016-08-01 17:27:01.793 demo[79533:3667104] 任务3---<NSThread: 0x7865da50>{number = 4, name = (null)}
2016-08-01 17:27:01.794 demo[79533:3667103] 任务2---<NSThread: 0x79a5a540>{number = 3, name = (null)}

2.异步任务+串行队列

- (void)viewDidLoad {
    [super viewDidLoad];
    
    //  只开一条新线程
    dispatch_queue_t queue = dispatch_queue_create("com.demo.chuanxing", NULL);
    dispatch_async(queue, ^{
        NSLog(@"任务1---%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"任务2---%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"任务3---%@",[NSThread currentThread]);
    });
}

打印

2016-08-01 17:29:13.092 demo[79710:3669166] 任务1---<NSThread: 0x7a236720>{number = 2, name = (null)}
2016-08-01 17:29:13.093 demo[79710:3669166] 任务2---<NSThread: 0x7a236720>{number = 2, name = (null)}
2016-08-01 17:29:13.094 demo[79710:3669166] 任务3---<NSThread: 0x7a236720>{number = 2, name = (null)}

3.同步任务+并发队列

- (void)viewDidLoad {
    [super viewDidLoad];

    //  不开新线程,当前是主线程,所以任务1、2、3在主线程中执行
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_async(queue, ^{
        NSLog(@"任务1---%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"任务2---%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"任务3---%@",[NSThread currentThread]);
    });
}

打印

2016-08-01 17:57:04.133 demo[81692:3691446] 任务1---<NSThread: 0x7a2314b0>{number = 1, name = main}
2016-08-01 17:57:04.134 demo[81692:3691446] 任务2---<NSThread: 0x7a2314b0>{number = 1, name = main}
2016-08-01 17:57:04.134 demo[81692:3691446] 任务3---<NSThread: 0x7a2314b0>{number = 1, name = main}

4.同步任务+串行队列

- (void)viewDidLoad {
    [super viewDidLoad];
    dispatch_queue_t queue = dispatch_queue_create("com.demo.chuanxing", NULL);
    dispatch_async(queue, ^{
        NSLog(@"新开的子线程---%@",[NSThread currentThread]);

        //  在新开的子线程中,在queue队列中测试
        dispatch_queue_t queue1 = dispatch_queue_create("com.demo.chuanxing", NULL);
        dispatch_sync(queue1, ^{
            NSLog(@"任务1---%@",[NSThread currentThread]);
        });
        dispatch_sync(queue1, ^{
            NSLog(@"任务2---%@",[NSThread currentThread]);
        });
        dispatch_sync(queue1, ^{
            NSLog(@"任务3---%@",[NSThread currentThread]);
        });
    });
}

打印

2016-08-01 17:53:10.405 demo[81376:3688029] 新开的子线程---<NSThread: 0x78e7fe80>{number = 2, name = (null)}
2016-08-01 17:53:10.406 demo[81376:3688029] 任务1---<NSThread: 0x78e7fe80>{number = 2, name = (null)}
2016-08-01 17:53:10.407 demo[81376:3688029] 任务2---<NSThread: 0x78e7fe80>{number = 2, name = (null)}
2016-08-01 17:53:10.407 demo[81376:3688029] 任务3---<NSThread: 0x78e7fe80>{number = 2, name = (null)}

5.异步任务+主队列(同3)
6.同步任务+主队列(切记不能在主队列中使用这种方式)

- (void)viewDidLoad {
    [super viewDidLoad];
    dispatch_queue_t queue = dispatch_queue_create("com.demo.chuanxing", NULL);
    dispatch_async(queue, ^{
        NSLog(@"新开的子线程---%@",[NSThread currentThread]);

        //  在新开的子线程中,任务1、2、3在主线程中执行
        dispatch_queue_t queue1 = dispatch_get_main_queue();
        dispatch_sync(queue1, ^{
            NSLog(@"任务1---%@",[NSThread currentThread]);
        });
        dispatch_sync(queue1, ^{
            NSLog(@"任务2---%@",[NSThread currentThread]);
        });
        dispatch_sync(queue1, ^{
            NSLog(@"任务3---%@",[NSThread currentThread]);
        });
    });
}

打印

2016-08-01 17:59:57.794 demo[81900:3693593] 新开的子线程---<NSThread: 0x79745ab0>{number = 2, name = (null)}
2016-08-01 17:59:57.798 demo[81900:3693419] 任务1---<NSThread: 0x79843a10>{number = 1, name = main}
2016-08-01 17:59:57.799 demo[81900:3693419] 任务2---<NSThread: 0x79843a10>{number = 1, name = main}
2016-08-01 17:59:57.800 demo[81900:3693419] 任务3---<NSThread: 0x79843a10>{number = 1, name = main}
2.4 GCD其它函数
  1. 栅栏函数 dispatch_barrier_async(queue, ^{ });
    在任务前面的异步任务都执行完后才会执行它,并且等它执行完后,后面的异步任务才能开始执行
    注意:
    1.>此函数加入全局队列示无效的,即queue不能是全剧队列
    2.>如果任务是同步的则会在当前的线程中执行,异步的话在新线程中执行

🍐

- (void)viewDidLoad {
    [super viewDidLoad];

    dispatch_queue_t queue = dispatch_queue_create("com.demo.bingfa", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        NSLog(@"任务1---%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"任务2---%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"任务3---%@",[NSThread currentThread]);
    });
    
    dispatch_barrier_async(queue, ^{
        NSLog(@"任务4---%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"任务5---%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"任务6---%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"任务7---%@",[NSThread currentThread]);
    });
}

打印(任务4总是在任务1、2、3和5、6、7的中间)

2016-08-01 20:55:44.075 demo[85715:3735784] 任务2---<NSThread: 0x7a8746e0>{number = 3, name = (null)}
2016-08-01 20:55:44.075 demo[85715:3735778] 任务1---<NSThread: 0x7a96ff10>{number = 2, name = (null)}
2016-08-01 20:55:44.075 demo[85715:3735777] 任务3---<NSThread: 0x7a67a470>{number = 4, name = (null)}
2016-08-01 20:55:44.077 demo[85715:3735777] 任务4---<NSThread: 0x7a67a470>{number = 4, name = (null)}
2016-08-01 20:55:44.078 demo[85715:3735777] 任务5---<NSThread: 0x7a67a470>{number = 4, name = (null)}
2016-08-01 20:55:44.078 demo[85715:3735778] 任务6---<NSThread: 0x7a96ff10>{number = 2, name = (null)}
2016-08-01 20:55:44.078 demo[85715:3735784] 任务7---<NSThread: 0x7a8746e0>{number = 3, name = (null)}

信号量的使用 --- 但以上任务2中存在其它异步并发任务,如网络请求的时:

   dispatch_queue_t queue = dispatch_queue_create("com.demo.bingfa", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
       
        // 创建一个信号量为0的信号(红灯)
        dispatch_semaphore_t sema = dispatch_semaphore_create(0);

        dispatch_queue_t queue2 = dispatch_queue_create("com.demo.bing", DISPATCH_QUEUE_CONCURRENT);

        dispatch_async(queue2, ^{
            [NSThread sleepForTimeInterval:2];
            // 使信号的信号量+1,这里的信号量本来为0,+1信号量为1(绿灯)
            dispatch_semaphore_signal(sema);
            NSLog(@"任务2---%@",[NSThread currentThread]);
        });
        NSLog(@"任务2---%@",[NSThread currentThread]);
        // 开启信号等待,设置等待时间为永久,直到信号的信号量大于等于1(绿灯)
        dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
    });
    dispatch_async(queue, ^{
        NSLog(@"任务3---%@",[NSThread currentThread]);
    });
    
    dispatch_barrier_async(queue, ^{
        NSLog(@"任务4---%@",[NSThread currentThread]);
    });

2.队列组
能实现在一个或则多个任务执行完之后,在执行特定的任务。例如:A任务和B任务并发执行,C任务需要在A任务和B任务都执行完之后再执行,这时候就可以通过队列组来实现
🍐

- (void)viewDidLoad {
    [super viewDidLoad];
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"任务1---%@",[NSThread currentThread]);
    });
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"任务2---%@",[NSThread currentThread]);
    });
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"任务3---%@",[NSThread currentThread]);
    });
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"任务4---%@",[NSThread currentThread]);
    });
    dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"任务5---%@",[NSThread currentThread]);
    });
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"任务6---%@",[NSThread currentThread]);
    });
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"任务7---%@",[NSThread currentThread]);
    });
}

打印: 你会发现任务5永远都是队列组中最后执行的一个

2016-08-01 22:10:55.218 demo[90162:3774195] 任务1---<NSThread: 0x7af959d0>{number = 2, name = (null)}
2016-08-01 22:10:55.218 demo[90162:3774194] 任务2---<NSThread: 0x7b1346f0>{number = 3, name = (null)}
2016-08-01 22:10:55.218 demo[90162:3774216] 任务6---<NSThread: 0x7b06cf00>{number = 6, name = (null)}
2016-08-01 22:10:55.218 demo[90162:3774215] 任务4---<NSThread: 0x7af95fe0>{number = 5, name = (null)}
2016-08-01 22:10:55.218 demo[90162:3774199] 任务3---<NSThread: 0x7ae30250>{number = 4, name = (null)}
2016-08-01 22:10:55.219 demo[90162:3774217] 任务7---<NSThread: 0x7b135bb0>{number = 7, name = (null)}
2016-08-01 22:10:55.220 demo[90162:3774217] 任务5---<NSThread: 0x7b135bb0>{number = 7, name = (null)}
3.NSOperation
3.1 概念

NSOperation是对GCD的封装;本身是一个抽象类,只能使用它的三个子类;和NSOperationQueue结合使用实现多线程并发

3.2 NSOperation和NSOperationQueue的理解
NSOperation三个子类

-NSInvocationOperation

- (void)viewDidLoad {
   [super viewDidLoad];
   //  不加入队列执行开始操作
   NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(addOperation:) object:@"NEW"];
   [operation start];
}
- (void)addOperation:(NSString *)name {
   //  在主线程中执行,没有意义
   NSLog(@"%@",[NSThread currentThread]);
}

-NSBlockOperation
创建操作时携带的任务在主线程,操作后面额外添加的任务,系统自动开启线程执行

- (void)viewDidLoad {
    [super viewDidLoad];
    //  不加入队列执行开始操作
    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        //  在主线程中执行
        NSLog(@"任务1---%@",[NSThread currentThread]);
    }];
    [operation addExecutionBlock:^{
        //  系统根据需要自动开启线程
        NSLog(@"任务2---%@",[NSThread currentThread]);
    }];
    [operation addExecutionBlock:^{
        //  系统根据需要自动开启线程
        NSLog(@"任务3---%@",[NSThread currentThread]);
    }];
    [operation addExecutionBlock:^{
        //  系统根据需要自动开启线程
        NSLog(@"任务4---%@",[NSThread currentThread]);
    }];
    [operation start];
}

-自定义NSOperation
一般操作过于复杂时可以使用自定义NSOperation,具有隐蔽性和可重复利用的好处

#import "JYOperation.h"
@implementation JYOperation
- (void)main {
    NSLog(@"%@",[NSThread currentThread]);
}
@end
- (void)viewDidLoad {
    [super viewDidLoad];
    //  不添加到队列的话,任务在主线程执行,没有意义
    JYOperation *operation = [[JYOperation alloc] init];
    [operation start];
}
NSOperationQueue

- 主队列
添加到主队列中的任务都在主线程中执行
-非主队列
任务在子线程中执行,控制任务以串行或并发的方式执行,具体通过设置队列的maxConcurrentOperationCount(最大并发数)属性来控制

注意:

1.maxConcurrentOperationCount必须在操作添加到队列之前设置才有效
2.maxConcurrentOperationCount 默认为-1,即默认任务是并发执行;等于0时,不执行任务,无意义;等于1时,串行执行任务;等于n时,并发数为n,允许n个任务是同时执行的

- (void)viewDidLoad {
    
    //  2.非主队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    queue.maxConcurrentOperationCount = 3;
    [queue addOperationWithBlock:^{
        NSLog(@"任务1---%@",[NSThread currentThread]);
    }];
    [queue addOperationWithBlock:^{
        NSLog(@"任务2---%@",[NSThread currentThread]);
    }];
    [queue addOperationWithBlock:^{
        NSLog(@"任务3---%@",[NSThread currentThread]);
    }];
    [queue addOperationWithBlock:^{
        NSLog(@"任务4---%@",[NSThread currentThread]);
    }];
    [queue addOperationWithBlock:^{
        NSLog(@"任务5---%@",[NSThread currentThread]);
    }];
    [queue addOperationWithBlock:^{
        NSLog(@"任务6---%@",[NSThread currentThread]);
    }];
}

打印

2016-08-02 10:10:56.874 demo[99820:3887770] 任务1---<NSThread: 0x7a98b340>{number = 2, name = (null)}
2016-08-02 10:10:56.874 demo[99820:3887814] 任务3---<NSThread: 0x7a71c690>{number = 4, name = (null)}
2016-08-02 10:10:56.874 demo[99820:3887769] 任务2---<NSThread: 0x7a71c450>{number = 3, name = (null)}
2016-08-02 10:10:56.875 demo[99820:3887814] 任务5---<NSThread: 0x7a71c690>{number = 4, name = (null)}
2016-08-02 10:10:56.876 demo[99820:3887810] 任务6---<NSThread: 0x7a71c840>{number = 5, name = (null)}
2016-08-02 10:10:56.876 demo[99820:3887770] 任务4---<NSThread: 0x7a98b340>{number = 2, name = (null)}
3.3 操作和队列结合(两种方式)
- (void)viewDidLoad {
    //  方式1:将操作加入队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"任务1---%@",[NSThread currentThread]);
    }];
    [queue addOperation:operation];
    
    //  方式2:通过队列直接加操作
    [queue addOperationWithBlock:^{
        NSLog(@"任务2---%@",[NSThread currentThread]);
    }];
}

补充(暂停、恢复和取消)

- (void)viewDidLoad {
    
    self.queue = [[NSOperationQueue alloc] init];
    //  任务暂停后可以恢复执行
    if (self.queue.isSuspended) {
        self.queue.suspended = NO;
    }
    //  任务取消后不可在恢复执行
    [self.queue cancelAllOperations];
}
3.4 设置操作依赖

通过调用操作[operation addDependency:]方法,可实现不同操作按指定的顺序执行

- (void)viewDidLoad {
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"操作1---");
    }];
    NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"操作2---");
    }];
    //  operation1依赖operation2,operation2执行完才能执行operation1
    [operation1 addDependency:operation2];
    [queue addOperation:operation1];
    [queue addOperation:operation2];
}

打印

2016-08-02 11:02:19.161 demo[3839:3930104] 操作2---
2016-08-02 11:02:19.162 demo[3839:3930104] 操作1---

🍐有一种需求:前两个异步任务都执行完之后才开始执行第三个
任务

- (void)viewDidLoad {
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"操作1---");
    }];
    NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"操作2---");
    }];
    NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"操作3---");
    }];

    [operation3 addDependency:operation1];
    [operation3 addDependency:operation2];
    [queue addOperation:operation1];
    [queue addOperation:operation2];
    [queue addOperation:operation3];
}

打印

2016-08-02 11:05:21.480 demo[4099:3932866] 操作2---
2016-08-02 11:05:21.480 demo[4099:3932865] 操作1---
2016-08-02 11:05:21.482 demo[4099:3932865] 操作3---
4 总结

以上主要涉及到开发中常用的一些内容,另外至于GCD和NSOperation线程间的通信,其实常用到的是异步操作后回到主线程,只要将需要回到主线程的任务添加到主队列即可

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,511评论 1 330
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,495评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,595评论 0 225
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,558评论 0 190
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,715评论 3 270
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,672评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,112评论 2 291
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,837评论 0 181
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,417评论 0 228
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,928评论 2 232
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,316评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,773评论 2 234
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,253评论 3 220
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,827评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,440评论 0 180
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,523评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,583评论 2 249

推荐阅读更多精彩内容