ios多线程编程知识小集

iOS为开发者提供了丰富的多线程编程机制,从最直接最简单的调用NSObjectperformSelector系列API,到NSThreadOperation Queues以及Dispatch Queues,当然iOS也支持更原始的pthread。本文主要讨论使用这些多线程编程机制时候的一些注意事项,具体如何使用这些机制,可以移步到Raywenderlich的教程以及官方文档:

  1. Grand Central Dispatch In-Depth: Part 1/2
  2. Grand Central Dispatch In-Depth: Part 2/2
  3. How To Use NSOperations and NSOperationQueues
  4. Concurrency Programming Guide

前两篇教程通过逐步改善一个图片集加载的例子,来说明GCD(Grant Central Dispatch)在数据读写同步、数据加载以及线程同步上的应用;类似的,第三篇教程通过一个加载table内容的例子,说明了NSOperation以及NSOperationQueue的应用。

NSOperation和NSOperationQueue

相比其他iOS多线程机制,NSOperation给开发者提供了更多独特的特性,这些特性包括:

  1. NSOperation将任务封装在对象中,完全支持OOP开发方式,有利于保持代码结构的一致性。

  2. NSOperation对象特性使得可以给任务线程设置相应的属性,比如NSOperation为开发者定义了三个属性isExecutingNSOperationisCanceled,当然开发者在子类化NSOperation时,可以根据需求定义额外的属性;之后开发者可以对这些属性进行KVO。

  3. 调用NSOperationcancel方法可以重置任务线程的isCanceled属性,任务根据这个状态作出相应的操作,比如退出或者暂停等;调用NSOperationQueuecancelAllOperations方法可以重置该队列上所有任务的isCanceled属性;

  4. 任务队列支持暂停操作,调用NSOperationQueuesetSuspended:(BOOL)可以暂停或者重启队列

  5. 任务之间支持互相依赖,通过调用NSOperationaddDependency:(NSOperation *)方法为自身任务添加依赖任务线程,调用removeDependency:(NSOperation *)可以将依赖队列移除

  6. 可以给任务队列中的任务设置运行优先级,从而控制任务队列中任务执行顺序

  7. 可以给任务设置一个completionBlock,该block在任务运行结束后执行。

下面总结几点在使用队列和任务时的几个要点:

1)NSOperation是一个抽象类,开发者需要继承该类并至少要重载main方法,

@interface CustomizedOperation : NSOperation
@property(strong) id  myData;
-(id)initWithData:(id)data;
@end

@implementation CustomizedOperation
- (id)initWithData:(id)data {
  if (self = [super init]){
    _myData = data;
  }
  return self;
}

- (void)main {
  @autoreleasepool {
    while (true) {
      // TODOs:
    }
  }
}
@end

2) 前面提到,可以调用cancel或者cancelAllOperations方法,但是这两个方法除了重置任务的isCanceled属性之外,不会做其他操作,要想达到真正取消任务的目的,需要开发者自己不断检查给属性的状态,并作出相应操作,如:

@interface CustomOperation: NSOperation
@end

@implementation CustomOperation
- (void)main {
  // 耗时操作
  @autoreleasepool {
    for (int i = 0 ; i < 1000000 ; i++) {

      // 检查任务状态
      if (self.isCancelled)
        break;

      // step 1
      [self doSomething];

      // 检查任务状态
      if (self.isCancelled)
        break;

      // step 2
      [self doSomethingelse];

    }
  }
}
@end

3) 任务默认都是同步执行的,如果想要任务异步执行,开发者需要自己维护任务的状态,需要重写start, isExecuting, isFinished, isConcurrent方法。其中重载start方法时不需要调用父类方法,isConcurrent方法只需要直接返回YES即可。

@interface MyOperation : NSOperation {
  BOOL        _executing;
  BOOL        _finished;
}
- (void)completeOperation;
@end

@implementation MyOperation
- (id)init {
  self = [super init];
  if (self) {
    _executing = NO;
    _finished = NO;
  }
  return self;
}

- (void)start {
  // 启动任务前检查任务是否被取消
  if ([self isCancelled])
  {
    // 如果任务被取消,则重置其他属性状态.
    [self willChangeValueForKey:@"isFinished"];
    _finished = YES;
    [self didChangeValueForKey:@"isFinished"];
    return;
  }
  [self willChangeValueForKey:@"isExecuting"];
  [NSThread detachNewThreadSelector:@selector(main) toTarget:self withObject:nil];
  _executing = YES;
  [self didChangeValueForKey:@"isExecuting"];
}

- (void)main {
  // 这里执行主要任务

  // 检查任务状态
  if (self.isCancelled)
    break;

  // step 1
  [self doSomething];

  // 检查任务状态
  if (self.isCancelled)
    break;

  // step 2
  [self doSomethingelse];

  [self completeOperation];
}

- (void)completeOperation {
  [self willChangeValueForKey:@"isFinished"];
  [self willChangeValueForKey:@"isExecuting"];
  _executing = NO;
  _finished = YES;
  [self didChangeValueForKey:@"isExecuting"];
  [self didChangeValueForKey:@"isFinished"];
}

- (BOOL)isConcurrent {
  return YES;
}

- (BOOL)isExecuting {
  return _executing;
}

- (BOOL)isFinished {
  return _finished;
}

GCD(Grant Central Dispatch)

1)dispatch queue的创建时候需要传递一个串行或者并行标志,GCD库提供了这两个标志,但是很多开发者却不使用,下面是正确的姿势:

dispatch_queue_t serialQueue = dispatch_queue_create("com.johnkui.serial", **DISPATCH_QUEUE_SERIAL**);
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.johnkui.concurrent", **DISPATCH_QUEUE_CONCURRENT**);

2)我们知道使用dispatch_group_async或者dispatch_group_enter/dispatch_group_leave配合dispatch_group_notify或者dispatch_group_wait可以实现同步操作,但是如果使用这些API不当的话,结果却会出人意料。下面是使用dispatch_group_async的正确姿势:

dispatch_queue_t queueA = dispatch_queue_create("com.johnkui.concurrentA", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t queueB = dispatch_queue_create("com.johnkui.concurrentB", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group = dispatch_group_create();

// 给任务组添加任务
dispatch_group_async(group, queueA, ^{
  // Some asynchronous work
});

// 给任务组添加其他任务
dispatch_group_async(group, queueB, ^{
  // Some other asynchronous work
});

// wait on the group to block the current thread.
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

dispatch_group_async配合dispatch_group_wait使用时,要保证dispatch_group_async调用的block必须运行相应的队列中,在上例中分为为queueA和queueB中,如果block里的任务实际运行在其他queue中,则dispatch_group_wait将立即返回而监测不到两个任务的完成状态,因为group只监控和该group关联的queue上的任务的运行状态。如果block里面的任务确实想运行在其他queue上,则可以使用dispatch_group_enter/dispatch_group_leave,下面是正确的姿势:

  dispatch_group_t group = dispatch_group_create();
  dispatch_queue_t queueA = dispatch_queue_create("com.johnkui.concurrentA", DISPATCH_QUEUE_CONCURRENT);
  dispatch_queue_t queueB = dispatch_queue_create("com.johnkui.concurrentB", DISPATCH_QUEUE_CONCURRENT);

  dispatch_group_enter(group);
  dispatch_async(queueA, ^{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
      // 执行耗时任务

      dispatch_group_leave(group);
    });
  });

  dispatch_group_enter(group);
  dispatch_async(queueB, ^{
    dispatch_async(dispatch_get_main_queue(), ^{
      UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Hello"
                                                          message:@"任务完成了!"
                                                         delegate:nil
                                                cancelButtonTitle:@"Ok"
                                                otherButtonTitles: nil];
      [alertView show];
      dispatch_group_leave(group);
    });
  });


  dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    NSLog(@"group is finished...");
  });

当block里的任务运行完需要更新UI时,一般都会这么操作:

    dispatch_async(dispatch_get_main_queue(), ^{
        //更新UI
    });

这个时候一定不要使用同步的dispatch_group_wait(group, DISPATCH_TIME_FOREVER),否则整个主线程将出现死锁,而是使用异步的dispatch_group_notify;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值