Welcome to Jekyll!

关于线程

在 iOS 中其实目前有 4 套多线程方案,他们分别是:

  • Pthreads

  • NSThread

  • GCD

  • NSOperation & NSOperationQueue


Pthreads

    #import <pthread.h>

    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    pthread_t thread;
    //创建一个线程并自动执行
    pthread_create(&thread, NULL, start, NULL);
	}

	void *start(void *data) {
    NSLog(@"%@", [NSThread currentThread]);

    return NULL;
	}

NSThread

	描述:它的生命周期还是需要我们手动管理,所以这套方案也是偶尔用用,比如 [NSThread currentThread],它可以获取当前线程类,你就可以知道当前线程的各种属性,用于调试十分方便。
  • 先创建线程类,再启动

    // 创建 NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:nil];

    // 启动 [thread start];

  • 创建并自动启动

    [NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:nil];
    
  • 使用 NSObject 的方法创建并自动启动

    [self performSelectorInBackground:@selector(run:) withObject:nil];

  • 其他方法

    //取消线程

    -(void)cancel;

    //启动线程

    -(void)start;

    //判断某个线程的状态的属性

    @property (readonly, getter=isExecuting) BOOL executing; @property (readonly, getter=isFinished) BOOL finished; @property (readonly, getter=isCancelled) BOOL cancelled;

    //设置和获取线程名字

    -(void)setName:(NSString *)n; -(NSString *)name;

    //获取当前线程信息

    +(NSThread *)currentThread;

    //获取主线程信息

    +(NSThread *)mainThread;

    //使当前线程暂停一段时间,或者暂停到某个时刻

    +(void)sleepForTimeInterval:(NSTimeInterval)time; +(void)sleepUntilDate:(NSDate *)date;



GCD

	它是苹果为多核的并行运算提出的解决方案,所以会自动合理地利用更多的CPU内核(比如双核、四核),最重要的是它会自动管理线程的生命周期(创建线程、调度任务、销毁线程),完全不需要我们管理,我们只需要告诉干什么就行。
任务和队列
	任务:即操作,你想要干什么,说白了就是一段代码,在 GCD 中就是一个 Block,所以添加任务十分方便。

	任务有两种执行方式: 同步执行 和 异步执行。

	同步(sync) 和 异步(async) 的主要区别在于会不会阻塞当前线程,直到 Block 中的任务执行完毕!
	同步(sync) 操作,它会阻塞当前线程并等待 Block 中的任务执行完毕,然后当前线程才会继续往下运行。
	异步(async)操作,当前线程会直接往下执行,它不会阻塞当前线程。

	队列:用于存放任务。一共有两种队列, 串行队列 和 并行队列。

	串行队列的任务,GCD 会 FIFO(先进先出) 地取出来一个,执行一个,然后取下一个,这样一个一个的执行。

	并行队列的任务,GCD 也会 FIFO的取出来,但不同的是,它取出来一个就会放到别的线程,然后再取出来一个又放到另一个的线程。这样由于取的动作很快,忽略不计,看起来,所有的任务都是一起执行的。不过需要注意,GCD 会根据系统资源控制并行的数量,所以如果任务很多,它并不会让所有任务同时执行。
同步执行 异步执行
串行队列 当前线程,一个一个执行 其他线程,一个一个执行
并行队列 当前线程,一个一个执行 开很多线程,一起执行

主队列

	这是一个特殊的 串行队列。什么是主队列,大家都知道吧,它用于刷新 UI,任何需要刷新 UI 的工作都要在主队列执行,所以一般耗时的任务都要放到别的线程执行。
	dispatch_queue_t queue = dispatch_get_main_queue();

自己创建的队列

	第一个参数是标识符,用于 DEBUG 的时候标识唯一的队列,可以为空。
	第二个参数用来表示创建的队列是串行的还是并行的
	  //串行队列
	  dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", NULL);
	  dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_SERIAL);
	  //并行队列
	  dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_CONCURRENT);

全局并行队列

	只要是并行任务一般都加入到这个队列。这是系统提供的一个并发队列。
	  dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

创建任务

  • 同步任务

    dispatch_sync(<#queue#>, ^{ //code here NSLog(@”%@”, [NSThread currentThread]); });

  • 异步任务

    dispatch_async(<#queue#>, ^{
    //code here
    NSLog(@"%@", [NSThread currentThread]); });
    

死锁

队列组

	队列组可以将很多队列添加到一个组里,这样做的好处是,当这个组里所有的任务都执行完了,队列组会通过一个方法通知我们。

	//1.创建队列组
	dispatch_group_t group = dispatch_group_create();

	//2.创建队列
	dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

	//3.多次使用队列组的方法执行任务, 只有异步方法
	//3.1.执行3次循环
	dispatch_group_async(group, queue, ^{
	    for (NSInteger i = 0; i < 3; i++) {
	        NSLog(@"group-01 - %@", [NSThread currentThread]);
	    }
	});

	//3.2.主队列执行8次循环
	dispatch_group_async(group, dispatch_get_main_queue(), ^{
	    for (NSInteger i = 0; i < 8; i++) {
	        NSLog(@"group-02 - %@", [NSThread currentThread]);
	    }
	});

	//3.3.执行5次循环
	dispatch_group_async(group, queue, ^{
	    for (NSInteger i = 0; i < 5; i++) {
	        NSLog(@"group-03 - %@", [NSThread currentThread]);
	    }
	});

	//4.都完成后会自动通知
	dispatch_group_notify(group, dispatch_get_main_queue(), ^{
	    NSLog(@"完成 - %@", [NSThread currentThread]);
	});

NSOperation和NSOperationQueue

	NSOperation 是苹果公司对 GCD 的封装,完全面向对象,所以使用起来更好理解。 大家可以看到 NSOperation 和 NSOperationQueue 分别对应 GCD 的 任务 和 队列 。特点:你不用管串行、并行、同步、异步这些名词。NSOperationQueue 有一个参数 maxConcurrentOperationCount 最大并发数,用来设置最多可以让多少个任务同时执行。当你把它设置为 1 的时候,就串行了。

	添加任务

	值得说明的是,NSOperation 只是一个抽象类,所以不能封装任务。但它有 2 个子类用于封装任务。分别是:NSInvocationOperation 和 NSBlockOperation 。创建一个 Operation 后,需要调用 start 方法来启动任务,它会 默认在当前队列同步执行。当然你也可以在中途取消一个任务,只需要调用其 cancel 方法即可。


	  //1.创建NSInvocationOperation对象
      NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];

  	  //2.开始执行
	  [operation start];

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

	  //2.开始任务
	  [operation start];



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

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

      //2.开始任务
      [operation start];


	创建队列
  • 主队列

      NSOperationQueue *queue = [NSOperationQueue mainQueue];
    
  • 创建一个其他队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];

      //2.创建NSBlockOperation对象
      NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
          NSLog(@"%@", [NSThread currentThread]);
      }];
    
      //3.添加多个Block
      for (NSInteger i = 0; i < 5; i++) {
          [operation addExecutionBlock:^{
              NSLog(@"第%ld次:%@", i, [NSThread currentThread]);
          }];
      }
    
      //4.队列添加任务
      [queue addOperation:operation];
    


		NSOperation 有一个非常实用的功能,那就是添加依赖
		有 3 个任务:A: 从服务器上下载一张图片,B:给这张图片加个水印,C:把图片返回给服务器。这时就可以用到依赖了:

		//1.任务一:下载图片
	NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
	    NSLog(@"下载图片 - %@", [NSThread currentThread]);
	    [NSThread sleepForTimeInterval:1.0];
	}];

	//2.任务二:打水印
	NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
	    NSLog(@"打水印   - %@", [NSThread currentThread]);
	    [NSThread sleepForTimeInterval:1.0];
	}];

	//3.任务三:上传图片
	NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
	    NSLog(@"上传图片 - %@", [NSThread currentThread]);
	    [NSThread sleepForTimeInterval:1.0];
	}];

	//4.设置依赖
	[operation2 addDependency:operation1];      //任务二依赖任务一
	[operation3 addDependency:operation2];      //任务三依赖任务二

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


	互斥锁 :给需要同步的代码块加一个互斥锁,就可以保证每次只有一个线程访问此代码块。
	@synchronized(self) 
	{
	    //需要执行的代码块
	}


	把多个线程都要执行此段代码添加到同一个串行队列,这样就实现了线程同步的概念。当然这里可以使用 GCD 和 NSOperation 两种方案,我都写出来。

	//GCD
  //需要一个全局变量queue,要让所有线程的这个操作都加到一个queue中
  dispatch_sync(queue, ^{
      NSInteger ticket = lastTicket;
      [NSThread sleepForTimeInterval:0.1];
      NSLog(@"%ld - %@",ticket, [NSThread currentThread]);
      ticket -= 1;
      lastTicket = ticket;
  });


  //NSOperation & NSOperationQueue
  //重点:1. 全局的 NSOperationQueue, 所有的操作添加到同一个queue中
  //       2. 设置 queue 的 maxConcurrentOperationCount 为 1
  //       3. 如果后续操作需要Block中的结果,就需要调用每个操作的waitUntilFinished,阻塞当前线程,一直等到当前操作完成,才允许执行后面的。waitUntilFinished 要在添加到队列之后!

  NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
      NSInteger ticket = lastTicket;
      [NSThread sleepForTimeInterval:1];
      NSLog(@"%ld - %@",ticket, [NSThread currentThread]);
      ticket -= 1;
      lastTicket = ticket;
  }];

  [queue addOperation:operation];

  [operation waitUntilFinished];

  //后续要做的事


延迟执行

NSObject:

  // 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, ^{
    // 3秒后需要执行的任务
});

NSTimer:

[NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:@selector(run:) userInfo:@"abc" repeats:NO];


GCD单例:

@interface Tool : NSObject

  • (instancetype)sharedTool;

@end

@implementation Tool

static id _instance;

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

    return _instance; }

@end



从其他线程回到主线程的方法

	//NSObject

	[self performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:NO];

	//GCD

	dispatch_async(dispatch_get_main_queue(), ^{

	});

	//NSOperationQueue

	[[NSOperationQueue mainQueue] addOperationWithBlock:^{

	}];
最近的文章

CocoaPods使用笔记

<font color = blue> 引入第三方的步骤</font> 新建 Podfile 文件 编辑 Podfile 文件(pod file 格式) pod install 去干别的事,等待完成。关闭项目,打开workSpace文件。下面是使用情况,我的工程叫做PicTool,接下来我的操作都是通过终端实现:➜ ~ cd /Users/zhengjiaohong/Desktop/_pod/PicTool ➜ PicTool touch Podfile...…

继续阅读
更早的文章

开发笔记1

开发细碎很多年前写的的,整理的时候看到了就顺便上传上来!___ <font color=green>关于pch</font> pch全称是“precompiled header”,也就是预编译头文件 使用:存放的工程中一些不常被修改的代码,比如常用的框架头文件 目的:提高编译速度 注意:添加的文件最好是是很少变动或不变动的头文件或者是预编译的代码片段; 用xcode创建pch文件:<img src= file:///Users/zhengjiaoh...…

继续阅读