iOS线程初探(四) GCD 和 NSOperation 小结

参考资料:关于iOS多线程,看我就够了

GCD

在GCD中,有两个概念很重要,那就是任务和队列。

  • 任务 :其实就是你需要做的事情,一个Block而已。任务有两种执行方式:同步执行和异步执行。同步执行:会阻塞当前线程,直至该任务执行完成后当前线程才接触阻塞,继续执行下面的任务。异步执行:执行任务时不会阻塞当前线程,当前线程会继续往下执行(这里有个小问题,如果我在主线程中异步调用一个任务,既然不阻塞当前线程,那么这个任务还是主线程执行?)。
  • 队列:用于存放任务。一共有两种队列,串行队列和并行队列。串行队列中的任务按照FIFO的顺序被GCD取出来,一个一个按序执行。并行队列中的任务也按照FIFO的顺序被GCD取出来,但是执行时会被放到另外的线程执行。

创建队列

  • 主队列:这是一个特殊的串行队列,任何UI的操作都在主队列中。
dispatch_queue_t queue = dispatch_get_main_queue();
  • 创建自己的队列:创建队列时,第一个参数是标识符,用于标识自己的队列,第二个参数表示串行还是并行,传入DISPATCH_QUEUE_SERIAL或者NULL表示串行,传入DISPATCH_QUEUE_CONCURRENT表示并行。
//串行队列
dispatch_queue_t queue = dispatch_queue_create("MyQueue", NULL);
dispatch_queue_t queue = dispatch_queue_create("MyQueue", DISPATCH_QUEUE_SERIAL);
//并行队列
dispatch_queue_t queue = dispatch_queue_create("MyQueue", DISPATCH_QUEUE_CONCURRENT);
  • 全局并行队列:只要是并行任务一般都加入到这个队列,这个队列是系统提供的并发队列。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  • 创建线程任务
    同步任务:会阻塞当前线程(SYNC)
dispatch_sync(queue, ^{
   //your code
   NSLog(@"%@", [NSThread currentThread]);
   });

异步任务:不会阻塞当前线程(ASYNC)

   dispatch_async(queue, ^{
   //your code
   NSLog(@"%@", [NSThread currentThread]);
   });
  • 队列组
    队列组可以将很多队列添加到一个组里,所有的任务完成后,队列组会通过一个方法通知我们。
//创建队列组
dispatch_group_t group = dispatch_group_create();
//创建队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

//使用队列组方法只有异步方法
//执行3次循环
dispatch_group_async(group, queue, ^{
    for(int i = 0; i < 3; ++i)
        NSLog(@"%@", task 1);
});

//主队列中执行循环2次
dispatch_group_async(group, dispatch_get_main_queue(), ^{
    for(int i = 0; i < 2; ++i)
        NSLog(@"%@", task 2);
 });

//执行4次循环
dispatch_group_async(group, queue, ^{
    for(int i = 0; i < 4; ++i)
        NSLog(@"%@", task 3);
});

//3个线程任务完成后自动通知
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    NSLog(@"Complete !");
});
  • 带阻塞队列的异步调用
   dispatch_barrier_async(_queue:dispatch_queue_t, _block:dispatch_block_t);

这个方法的重点是你传入的queue,当你传入的是通过DISPATCH_QUEUE_CONCURRENT参数自己创建的queue时,这个方法会阻塞这个queue(注意,是阻塞queue,而不是当前线程),直到这个queue中在_block之前的任务被执行完后才开始执行自己,自己执行完毕后,才取消这个queue的阻塞,使这个queue后面的任务得以被执行。但是如果你传入的是其它的队列,那么这个函数和dispatch_async没有区别了

dispatch_barrier_sync(_queue: dispatch_queue_t, _block:dispatch_block_t);

这个方法的使用和上面的一样,不同的是这个方法不仅阻塞队列,还会阻塞当前线程。如果你传入的是其它的queue,那么它就和dispatch_sync一样了。

NSOperation和NSOperationQueue

NSOpreation是Apple对GCD的再封装,完全面向对象。NSOperation和NSOpreationQueue分别对应GCD的任务和队列。
我们需要做的就是将任务封装到NSOperation对象中,再将此任务添加到NSOperationQueue对象中即可。具体操作往下看吧。

添加任务

需要说明的是,NSOperation只是一个抽象类,不能封装任务。我们用它的两个子类来封装任务,这两个子类分别是:NSInvocationOperation和NSBlockOperation。创建一个operation后,需要调用start方法来启动任务,它会默认在当前队列中同步执行。取消任务需要调用cancel方法。

  • NSInvocationOperation 需要传入一个方法名(任务)
//创建NSInvocationOperation对象
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
//执行任务
[operation start];
  • NSBlockOperation
//创建NSBlockOperation对象
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock: ^{
    NSLog(@"%@", [NSThread currentThread]);
}];

//开始任务
[operation start];

之前说过,这样封装的任务默认会在当前线程同步执行。但是NSBlockOperation还有个方法是addExecutionBlock:,通过这个方法可以给Operation添加多个执行Block。这样Operation中的任务会并发执行,它会在主线程和其它多个线程执行这些任务。

//创建NSBlockOperation对象
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock: ^{
    NSLog(@"%@", [NSThread currentThread]);
}];

//添加多个Block
for (int i = 0; i < 5; ++i)
{
    [operation addExecutionBlock: ^{
        NSLog(@"task %d: %@", i, [NSThread currentThread]);
    }];
}
//开始任务
[operation start];

注意:addExecutionBlock方法必须在start之前添加,否则会报错。

  • 自定义Operation
    除了上面的两种 Operation 以外,我们还可以自定义 Operation。自定义 Operation 需要继承 NSOperation 类,并实现其 main() 方法,因为在调用 start() 方法的时候,内部会调用 main() 方法完成相关逻辑。所以如果以上的两个类无法满足你的欲望的时候,你就需要自定义了。你想要实现什么功能都可以写在里面。除此之外,你还需要实现 cancel() 在内的各种方法。

创建队列

这里的队列分为主队列和其他队列。只要任务添加到队列,就会自动调用start方法。

  • 主队列
NSOperationQueue *queue = [NSOperationQueue mainQueue];
  • 其它队列
    其它队列与主队列通过一个类方法来获取的方式不同,通过初始化产生的队列就是其它队列。
    注意:其它队列的任务会在其它线程并行执行。
//创建一个非主队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];

//创建NSBlockOperation对象
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock: ^{
    NSLog(@"%@", [NSThread currentThread]);
}];

//添加多个Block
for(int i = 0; i < 5; ++i)
{
    [operation addExecutionBlock:^{
        NSLog(@"task %d : %@", i, [NSThread currentThread]);
    }];
}

//添加任务到队列中
[queue addOperation :operation];

NSOperationQueue中有一个参数maxConcurrentOperationCount,用它来控制并发数量,如果并发数量为1,那么就是串行,否则是并行。NSOperationQueue中还有一个添加任务的方法 addOperationWithBolck:(void(^)(void))block。

添加依赖

NSOperation中可以为任务添加依赖。比如A任务完成后才执行B任务,B任务完成后执行C任务。

//mission 1
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"task 1 : %@", [NSThread currentThread]);
    [NSThread sleepForTimeInterval:1.0f];
}];
//mission 2
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"task 2 : %@", [NSThread currentThread]);
    [NSThread sleepForTimeInterval:1.0f];
}];
//mission 3
NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"task 3 : %@", [NSThread currentThread]);
    [NSThread sleepForTimeInterval:1.0f];
}];

//设置依赖
[operation2 addDependency:operation1];
[operation3 addDependency:operation2];

//创建队列并加入任务
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperations:@[operation3, operation2, operation1] waitUntilFinished:NO];

注意别互相依赖,会导致死锁
可以使用removeDependency来解除依赖关系
依赖只与任务有关,与队列无关

其它方法

  • NSOperation

    BOOL executing; //判断任务是否正在执行
    BOOL finished; //判断任务是否完成
    void (^completionBlock)(void); //用来设置完成后需要执行的操作
    (void)cancel; //取消任务
    (void)waitUntilFinished; //阻塞当前线程直到此任务执行完毕

  • NSOperationQueue

    NSUInteger operationCount; //获取队列的任务数
    (void)cancelAllOperations; //取消队列中所有的任务
    (void)waitUntilAllOperationsAreFinished; //阻塞当前线程直到此队列中的所有任务执行完毕
    [queue setSuspended:YES]; // 暂停queue
    [queue setSuspended:NO]; // 继续queue

延时执行

  • perform
 //3秒后自动调用self的run方法,并传参数@"abc"
[self performSelector:@selector(run:) withObject:@"abc" afterDelay:3];
  • GCD
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
double delay = 3;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay *    NSEC_PER_SEC)), queue, ^{
    //your code , execute after 3sec
});
  • NSTimer
[NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:@selector(run:) userInfo:@"abc", repeats:NO];

单例模式

标准实现方法

@interface Tool: NSObject <NSCopying>
+ (instancetype)shareTool;
@end

@implementation Tool

static id _instance;

+(instancetype)shareTool
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [[Tool alloc] init];
    });
    return _instance;
}

@end

从其它线程回到主线程

  • NSThread
[self performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:NO];
  • GCD
dispatch_async(dispatch_get_main_queue(), ^{

});
  • NSOperationQueue
[[NSOperationQueue mainQueue] addOperationWithBlock:^{

}];
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值