zoukankan      html  css  js  c++  java
  • iOS

    1、Threads

    1.1 进程

    • 进程是指在系统中正在运行的一个应用程序。每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内。

      • 比如同时打开 QQ、Xcode,系统就会分别启动两个进程。通过 “活动监视器” 可以查看 Mac 系统中所开启的进程。

        Threads1

      • 一个程序的一次运行,在执行过程中拥有独立的内存单元,而多个线程共享一块内存。

    1.2 线程

    • 线程是进程中执行任务的基本执行单元。一个进程要执行任务,必须得有线程,一个进程(程序)的所有任务都在线程中执行。每一个进程至少有一条线程,即主线程。一个进程可以开启多条线程,每条线程可以并发(同时)执行不同的任务。

      • 比如使用酷狗播放音乐、使用迅雷下载电影,都需要在线程中执行。

        Threads2

    • 在程序中每一个方法的执行,都是从上向下串行执行的。除非使用 block,否则在一个方法中,所有代码的执行都在同一个线程上。

    • 进程与线程的联系:线程是进程的基本组成单位。

    • 进程与线程的区别:

        1. 调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位。
        1. 并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可并发执行。
        1. 拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源。
        1. 系统开销:在创建或撤消进程时,由于系统都要为之分配和回收资源,导致系统的开销明显大于创建或撤消线程时的开销。

    1.2.1 线程的串行

    • 一个线程中任务的执行是串行(顺序执行)的。如果要在一个线程中执行多个任务,那么只能一个一个的按顺序执行这些任务,也就是说,在同一时间内,一个线程只能执行一个任务。

      • 比如在一个线程中下载三个文件(分别是文件 A,文件 B,文件 C)。

        Threads3

    1.2.2 多线程原理

    • 同一时间,CPU 只能处理一条线程,只有一条线程在工作(执行)。多线程并发(同时)执行,其实是 CPU 快速的在多条线程之间调度(切换)。如果 CPU 调度线程的时间足够快,就造成了多条线程并发执行的假象。

      Threads4

    • 多线程开发的复杂度相对较高,在开发时可以按照以下套路编写代码:

      • 1、首先确保单个线程执行正确。
      • 2、添加线程。
    • 在多线程开发的时候,有几点提示:

      • 1、不要相信一次运行的结果。
      • 2、不要去做不同线程之间执行的比较,线程内部的方法都是各自独立执行的。
      • 3、多线程的目的是将耗时的操作放在后台,不阻塞主线程和用户的交互。
      • 4、多线程开发的原则是简单,不要将简单的事情搞复杂了。

    1.2.3 多线程优点

    • 使用多线程可以将耗时的任务放到后台去执行,不阻塞主线程和用户的交互。
    • 使用多线程能够并发、同时执行多个任务,可以适当提高程序的执行效率,适当提高资源的利用率(CPU、内存的利用率)。

    1.2.4 多线程缺点

    • 每开一个线程都会造成系统额外的负担,开启线程需要占用一定的内存空间(默认情况下,每一条线程都会占用 512KB),如果开启大量的线程,会占用大量的内存空间,降低程序的性能。

    • 线程越多,CPU 调度线程上开销就越大。

    • 线程越多,程序设计就更加复杂,比如线程之间的通信、多线程的数据共享就更加复杂。

    • 通常开启 5 到 6 条线程即可,不能开启的太多。

      • 栈区:保存局部变量和函数的参数,由下向上压栈。线程执行完毕后,栈区会自动释放,程序员不需要管理栈区的内存。

        Threads5

    1.2.5 主线程

    • 一个 iOS 程序运行后,默认会开启一条线程,称为主线程或 UI 线程。主线程主要用于显示/刷新 UI 界面,处理 UI 事件(比如点击事件、滚动事件、拖拽事件等)。

    • 注意别将比较耗时的操作放到主线程中,耗时的操作会卡住主线程,严重影响 UI 的流畅度,给用户一种 “卡” 的坏体验。

    1.2.6 线程状态

    • 新线程创建启动后,线程对象被加入可调度线程池中,进入就绪状态,等待 CPU 调度。在线程被调度运行时,如果线程调用了 sleep 方法或者等待同步锁时,线程由运行状态进入到阻塞状态,线程被移出可调度线程池。sleep 到时或者得到同步锁时,线程重新进入可调度线程池,回到就绪状态。线程任务执行完毕或者异常、强制退出时,线程由运行状态进入到死亡状态,线程结束,线程占用内存空间被释放。

      Threads6

      • 在整个线程状态循环中,程序员无法控制线程的运行状态。线程是否运行由 CPU 调度完成。

    1.2.7 线程安全

    • 如果一个属性,在多个线程执行的情况下,仍然能够得到正确结果,被称为线程安全。

    • 要实现线程安全,就必须要用到锁,用到锁就会降低程序的性能。为了得到更佳的用户体验,UIKit 不是线程安全的,UI 线程约定所有更新 UI 的操作都必须主线程上执行,因此,主线程又被称为 UI 线程。

      • 1)iOS 开发建议:

        • 所有属性都声明为 nonatomic。
        • 尽量避免多线程抢夺同一块资源。
        • 尽量将加锁、资源抢夺的业务逻辑交给服务器端处理,减小移动客户端的压力。
      • 2)使用互斥锁:

        	@synchronized (self) {
        
        	}
        
        • 互斥锁能够保证锁定范围的代码,同一时间,只有一条线程能够执行。

        • 互斥锁的锁定范围,应该尽量小,只要锁住资源读写部分的代码即可。锁定范围越大,效率越差。使用锁之后,程序执行性能都会受到影响,会影响并发的目的。

        • 在 iOS 开发中,使用锁的机会极少,一般都是从服务器获取到数据,直接显示。

        • 参数:

          • self 表示当前的 UIViewController,为 NSObject 的子类。本质上是任意一个 NSObject 子类对象都可以当成锁。
          • 如果代码中只有一个地方需要加锁,大多都使用 self,这样可以避免单独再创建一个锁对象。
          • 锁对象一定要保证所有的线程都能够访问。必须为全局变量。
      • 3)使用变量原子性:

        • 原子属性(线程安全),是针对多线程设计的,是默认属性。

        • 多个线程在写入原子属性时(调用 setter 方法),能够保证同一时间只有一个线程执行写入操作,但是允许多个线程同时读取属性值。

        • 原子属性是一种单(线程)写多(线程)读的多线程技术,在多线程读取数据时,有可能出现“脏”数据 - 读取的数据可能会不正确。

        • 原子属性内部也有一把 "锁",是自旋锁,执行效率比互斥锁高。

        • 在定义属性时,如果不需要考虑线程安全,要显示地指定为 nonatomic。

        • 原子属性,解决不了卖票问题,因为卖票的读写都需要锁定。

        • 如果重写了原子属性的 setter 方法,相当于覆盖了系统提供的 setter 方法,此时,系统要求,必须重写 getter 方法。

        • 定义属性时,系统默认提供 getter & setter 方法,并且生成一个 _成员变量,但是如果自己重写了 getter & setter 方法,_成员变量,不会自动生成。

        • @synthesize 合成指令,可以指定保存属性数值的 成员变量。

        • 在 Xcode 4.5 之前,开发,程序员必须自己实现 @synthesize 指令。

      • 4)自旋锁 & 互斥锁:

        • 共同点:

          • 都能够保证同一时间,只有一条线程执行锁定范围的代码。
        • 不同点:

          • 互斥锁:如果发现有其他线程正在执行锁定的代码,线程会进入休眠状态,等待其他线程执行完毕,打开锁之后,线程会被唤醒。
          • 自旋锁:如果发现有其他线程正在执行锁定的代码,线程会以死循环的方式,一直等待锁定代码执行完成。
        • 结论:

          • 自旋锁更适合执行非常短的代码。
          • 无论什么锁,都是要付出代价。

    1.2.8 线程间通信

    • 在子线程中执行比较耗时的操作(如下载图片等),子线程执行完毕后通知主线程更新 UI 等的操作。

    • UIKit 中几乎所有控件都不是线程安全的,因此需要在主线程上更新 UI。在子线程中可以使用以下方法让主线程执行 UI 操作:

      	// waitUntilDone: YES 等待主线程执行完成后子线程再继续执行
      	[self performSelectorOnMainThread:@selector(updateUI:) withObject:image waitUntilDone:YES];
      
      	dispatch_async(dispatch_get_main_queue(), ^{
      
      		[self updateUI:image];
      	});
      
      	[[NSOperationQueue mainQueue] addOperationWithBlock:^{
      
          	[self updateUI:image];
      	}];
      

    1.3 iOS 中多线程实现方案

    Threads7

    1.3.1 pthread

    • pthread 是 POSIX 的多线程开发框架,由于是跨平台的 C 语言框架,在苹果的头文件中并没有详细的注释。Xcode 中所用跨平台的文件通常都在 usr/include 目录下。对于所有跨平台的框架(如:pthread & socket),可以访问一个网站: baike.baidu.com

    • iOS 中 C 语言框架:

      • 在 C 语言中,没有对象的概念,对象是以结构体的方式来实现的。

      • 通常,在 C 语言框架中,“对象” 类型以 _t/Ref 结尾,而且定义时不需要使用 * 。

      • 内存管理:

        • 在 OC 中,如果是 ARC 开发,编译器会在编译时,根据代码结构,自动添加 retain/release/autorelease。
        • 但是,ARC 只负责管理 OC 部分的内存管理,而不负责 C/C++ 语言部分代码的内存管理。
        • 如果开发的时候,涉及到混合语言开发,如果使用的 C 语言框架出现 retain/create/copy/new 等字样的函数,大多都需要程序员手动 release,否则会出现内存泄漏。
      • 参数桥接:

        • 在混合开发时,如果在 C 和 OC 之间传递数据,需要使用 __bridge 进行桥接,告诉编译器如何管理内存。__bridge 就是保留原有的管理方式。
        • 桥接的添加可以借助 Xcode 的辅助功能添加。
        • MRC 中不需要使用桥接。MRC 中所有内存都是程序员负责的。
        • 管理的是堆区的内存。alloc/copy/retain 等字样的函数都是和堆区有关的。
      • void *:

        • C 语言中的 void * 和 OC 中的 id 是等价的。

        • 例如:

          	C     : void *(*)(void *)
          	OC    : id (函数名) (id)       即 返回值类型(函数名)(参数)
          	block : 返回值 (^) (参数)       block 匿名的函数指针
          

    1.3.2 NSThread

    • 优点:NSThread 轻量级。
    • 缺点:需要自己管理线程的生命周期,线程同步。线程同步对数据的加锁会有一定的系统开销。

    1.3.3 GCD

    • GCD 是 Grand Central Dispatch(译为 “中枢调度器”)的简称,它是基于 C 语言编写的,是苹果公司为多核的并行运算提出的解决方案。GCD 在工作时会自动利用更多的处理器核心,以充分利用更强大的机器。如果使用 GCD,完全由系统管理线程,我们不需要编写线程代码,只需定义想要执行的任务,然后添加到适当的调度队列(dispatch queue),GCD 会负责创建线程和调度你的任务。它首次发布在 Mac OS X 10.6 ,iOS 4 上。在 iOS 所有实现多线程的方案中,GCD 应该是最有魅力的,GCD 是一个替代诸如 NSThread, NSOperationQueue,NSInvocationOperation 等技术的很高效和强大的技术。

      1. 工作原理:
      • 将长期运行的任务拆分成多个工作单元,并将这些单元添加到 dispath queue 中,系统会为我们管理这些 dispath queue,根据可用的处理资源,安排他们在任何可用的处理器核心上执行任务。我们不需要直接启动和管理后台线程。一个任务可以是一个函数(function)或者是一个 block。GCD 的底层依然是用线程实现,不过这样可以让程序员不用关注实现的细节。

      • 将任务添加到队列,并且指定执行任务的函数,执行任务。任务使用 block 封装,任务的 block 没有参数也没有返回值。

      1. 执行任务的函数:
      • 异步 dispatch_async

        • 不用等待当前语句执行完毕,就可以执行下一条语句。
        • 会开启线程执行 block 的任务。
        • 异步是多线程的代名词。
      • 同步 dispatch_sync

        • 必须等待当前语句执行完毕,才会执行下一条语句。

        • 不会开启线程。

        • 在当前线程执行 block 的任务。

        • 同步任务的作用:同步任务,可以让其他异步执行的任务,"依赖" 某一个同步任务。例如:在用户登录之后,再异步下载文件!

      1. 任务调度队列:
      • GCD 中的 FIFO 队列称为 dispatch queue(调度队列)。系统提供了许多预定义的 dispatch queue,包括可以保证始终在主线程上执行工作的 dispatch queue。也可以创建自己的 dispatch queue,而且可以创建任意多个。GCD 的 dispatch queue 严格遵循 FIFO(先进先出) 原则,添加到 dispatch queue 的工作单元将始终按照加入 dispatch queue 的顺序启动。dispatch queue 按先进先出的顺序,串行或并发地调度任务在线程上实行。

      • dispatch queue 分为下面三种:

        • Serial:

          • 串行队列,又称为 private dispatch queues,一次只能 "调度" 一个任务, 当前任务完成才开始出列并启动下一个任务。Serial queue 通常用于同步访问特定的资源或数据。当你创建多个 Serial queue 时,虽然它们各自是同步执行的,但 Serial queue 与 Serial queue 之间是并发执行的。
        • Concurrent:

          • 并发队列,又称为 global dispatch queues,一次可以 "调度" 多个任务,尽可能多地启动任务并发执行,任务执行完成的顺序是随机的。

          • 系统给每一个应用程序提供了三个 concurrent dispatch queues。这三个并发调度队列是全局的,它们只有优先级的不同。因为是全局的,我们不需要去创建。我们只需要通过使用函数 dispath_get_global_queue 去得到队列。

        • Main dispatch queue:

          • 主队列,它是全局可用的 serial queue,专门用来在主线程上调度任务的队列。不会开启线程,主队列异步执行时如果主线程上正在有代码执行,就不会调度队列中的任务,等待主线程空闲之后再调度任务。主线程中主队列同步执行时,主队列和主线程相互等待会造成死锁。
      1. 各种队列的执行效果:

        Type | 全局并发队列 | 手动创建串行队列 | 主队列
        -----------------|--------------------|--------------------|-------------------
        同步 (sync) | 没有开启新线程 | 没有开启新线程 | 没有开启新线程
        ~ | 串行执行任务 | 串行执行任务 | 串行执行任务
        异步 (async) | 有开启新线程 | 有开启新线程 | 没有开启新线程
        ~ | 并发执行任务 | 串行执行任务 | 串行执行任务

      • 开不开线程由执行任务的函数决定:

        • 异步开,异步是多线程的代名词。
        • 同步不开。
      • 开几条线程由队列决定:

        • 串行队列开一条线程。

        • 并发队列开多条线程。

        • 主队列不会开启线程。

      1. 队列的选择:
      • 多线程的目的:将耗时的操作放在后台执行!

      • 串行队列,只开一条线程,所有任务顺序执行

        • 如果任务有先后执行顺序的要求。
        • 效率低 -> 执行慢 -> "省电"。
        • 有的时候,用户其实不希望太快!例如使用 3G 流量,"省钱"。
      • 并发队列,会开启多条线程,所有任务不按照顺序执行

        • 如果任务没有先后执行顺序的要求。
        • 效率高 -> 执行快 -> "费电"。
        • WIFI,包月。
      • 实际开发中

        • WIFI 线程数 6 条。
        • 3G / 4G 移动开发的时候,2~3 条,再多会费电费钱。
      1. 全局队列 & 并发队列的区别:
      • 全局队列:

        • 没有名称。
        • 无论 MRC & ARC 都不需要考虑释放。
        • 日常开发中,建议使用 "全局队列"。
      • 并发队列:

        • 有名字,和 NSThread 的 name 属性作用类似。
        • 如果在 MRC 开发时,需要使用 dispatch_release(q); 释放相应的对象。
        • 开发第三方框架时,建议使用并发队列。
      1. GCD & NSThread 对比:
      • GCD 所有的代码写在一起的,让代码更加简单,易于阅读和维护。
        • NSThread 通过 @selector 指定要执行的方法,代码分散。
        • GCD 通过 block 指定要执行的代码,代码集中。
      • 使用 GCD 不需要管理线程的创建/销毁/复用的过程。程序员不用关心线程的生命周期。
        • NSThread 需要自己创建线程对象,并且指定 selector 方法,然后 start。
        • GCD 只需要将任务添加给队列,并且指定执行的函数
      • 如果要开多个线程 NSThread 必须实例化多个线程对象。
      • NSThread 靠 NSObject 的分类方法实现的线程间通讯,GCD 靠 block。

    1.3.4 NSOperation

    • NSOperation 也是苹果公司推出的 “并发” 技术。是基于 OC 语言的,iOS 2.0 推出。GCD 推出之后,苹果对 NSOperation 底层重新编写过,是对 GCD 的封装。Cocoa operation 相关的类是 NSOperation,NSOperationQueue。NSOperation 是个抽象类,使用它必须用它的子类,可以实现它或者使用它定义好的两个子类:NSInvocationOperation 和 NSBlockOperation。

    • 优点:不需要关心线程管理,数据同步的事情,可以把精力放在自己需要执行的操作上。

    • 1)NSOperation 与 GCD 对比:

      • NSOperation:

        • 核心概念:把 "操作(异步执行的任务)" 添加到队列(全局的并发队列)。即创建 NSOperation 子类的对象,把对象添加到 NSOperationQueue 队列里执行。

        • OC 的框架,更加面向对象,是对 GCD 的封装

        • 高级功能:

          • 最大操作并发数(GCD 不好做)
            • 在 iOS 7.0 之前,使用 GCD & NSOperation 能够开启的线程数都不多,最大的线程数一般只有 5~6 条
            • 从 iOS 8.0 开始,能够开很多个线程,如果不控制,会造成资源浪费
          • 继续/暂停/全部取消
          • 能够指定任务的 "依赖" 关系(GCD 中,同步任务是来指定依赖关系)
      • GCD:

        • 核心概念:将 "任务(block)" 添加到队列(串行/并发/全局/主队列),并且指定任务执行的函数(同步/异步)

        • C 语言的框架,dispatch_xxx 函数

        • 高级功能:

          • 一次性 once
          • 延迟操作 after
          • 调度组 (op 可以做,但是做不了太复杂)
    • 2)自定义 NSOperation 操作流程:

      Threads8

    2、pthread 的使用

    2.1 pthread 线程创建

    • Objective-C

      	// 添加头文件
      	
      		#import <pthread.h>
      
      	// 创建 pthread 子线程
      	/*
          	int pthread_create(pthread_t * __restrict, 
          	        const pthread_attr_t * __restrict, 
          	                        void *(*)(void *), 
          	                        void * __restrict);
      
          	返回值:
      
              	线程创建成功,返回 0。线程创建失败,返回出错编号。
              	成功的原因只有一个,失败的原因可以有很多,在很多 C 语言框架中,都会采用这个套路。
      
          	参数:
      
              	pthread_t *       :第一个参数为指向线程标识符的指针。
              	pthread_attr_t *  :第二个参数用来设置线程属性。
              	void *(*)(void *) :第三个参数是线程运行函数的起始地址。
              	void *            :第四个参数是运行函数的参数。
      	*/
      
          	pthread_t threadId = NULL;
          	NSString *str = @"pthread";
          
          	int result = pthread_create(&threadId, NULL, pthreadDemo, (__bridge void *)(str));
          
          	if (result) {
              	NSLog(@"线程创建失败 %d", result);
          	} else {
              	NSLog(@"线程创建成功");
          	}
      
      	// 子线程执行方法
      
          	void * pthreadDemo(void * param) {
              
              	NSString *str = (__bridge NSString *)(param);
              
              	NSLog(@"%@ --- %@", [NSThread currentThread], str);
              
              	return NULL;
          	}
      

    3、NSThread 的使用

    3.1 NSThread 线程创建

    	- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument;
    	+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument;
    	- (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg;
    
    	public convenience init(target: AnyObject, selector: Selector, object argument: AnyObject?)
    	public class func detachNewThreadSelector(selector: Selector, toTarget target: AnyObject, withObject argument: AnyObject?)
    	public func performSelectorInBackground(aSelector: Selector, withObject arg: AnyObject?)
    
    	参数的意义:
    
    		selector :线程执行的方法,这个 selector 只能有一个参数,而且不能有返回值。
    		target   :selector 消息发送的对象。
    		argument :传输给 target 的唯一参数,也可以是 nil。
    
    	第一种方式是先创建线程对象,然后再运行线程操作,在运行线程操作前可以设置线程的优先级等线程信息。
    	第二种和第三种方式会直接创建线程并且开始运行线程。
    	第三种方式是 "NSObject" 的一个分类方法,可以由任何继承自 "NSObject" 的类对象调用该方法隐式创建并启动一个子线程。
    
    • Objective-C

      	// 1. 创建一个子线程
      
          	NSThread *myThread = [[NSThread alloc] initWithTarget:self selector:@selector(downloadImage:) object:imageUrlPath];
          	[myThread start];                                                                                                       	
      
      	// 2. 创建并启动一个子线程
      
          	[NSThread detachNewThreadSelector:@selector(downloadImage:) toTarget:self withObject:imageUrlPath];
      
      	// 3. 隐式创建并启动一个子线程
      
          	[self performSelectorInBackground:@selector(downloadImage:) withObject:imageUrlPath];
      
      	// 子线程执行方法
      
      		- (void)downloadImage:(NSString *) urlPath {
      
      		}
      
    • Swift

      	// 1. 创建一个子线程
      
          	let myThread = NSThread(target: self, selector: #selector(ViewController.downloadImage(_:)), object: imageUrlPath)
          	myThread.start()                                                                                                      	
      
      	// 2. 创建并启动一个子线程
      
          	NSThread.detachNewThreadSelector(#selector(ViewController.downloadImage(_:)), toTarget: self, withObject: imageUrlPath)
      
      	// 3. 隐式创建并启动一个子线程
      
          	self.performSelectorInBackground(#selector(ViewController.downloadImage(_:)), withObject: imageUrlPath)
              
      	// 子线程执行方法
      
      		func downloadImage(urlPath: String) {
      	
      		}
      

    3.2 NSThread 线程设置

    • Objective-C

      	// 启动子线程
      	/*
      		将线程对象加入可调度线程池等待 CPU 调度,线程执行完毕后,由于内存空间已经被释放,不能再次启动
      	*/
      	[myThread start];
      
      	// 通知线程取消
      	/*
      		可以在外部终止线程执行,在线程执行方法中需要增加 isCancelled == YES,如果成立直接返回
      	*/
      	[myThread cancel];
      
      	// 获取线程名字
      	NSString *threadName = myThread.name;
      
      	// 设置线程名字
      	/*
      		在多个线程开发时,可以用来判断到底是谁在执行任务
      		在大的商业项目中,通常需要在程序崩溃时,获取程序准确执行所在的线程
      	*/
      	myThread.name = @"downloadImage";
      
      	// 设置线程的优先级
      	/*
      		范围 0.0 到 1.0,默认为 0.5,优先级高表示 CPU 调度的频率相对较高。开发时尽量不要修改
      	*/
      	myThread.threadPriority = 1;
      
      	// 判断线程是否正在执行 readonly
      	BOOL isExecuting = myThread.isExecuting;
      
      	// 判断线程是否完成 readonly
      	BOOL isFinished = myThread.isFinished;
      
      	// 判断线程是否被取消 readonly
      	BOOL isCancelled = myThread.isCancelled;
      
      	// 获取当前线程
      	NSThread *currentThread = [NSThread currentThread];
      
      	// 判断当前线程是否为主线程
      	/*
      		可以在所有的多线程技术中使用
      	*/
      	BOOL isMainThread = [[NSThread currentThread] isMainThread];
      	BOOL isMainThread = [NSThread isMainThread];
      
      	// 判断是否为多线程操作
      	BOOL isMultiThreaded = [NSThread isMultiThreaded];
      
      	// 引用主线程
      	/*
      		返回主线程对象
      	*/
      	NSThread *mainThread = [NSThread mainThread];
      
      	// 获取栈区大小
      	/*
      		线程执行前,主线程和子线程默认栈区大小都为 512K,线程完成后,栈区大小 0K,内存空间被释放
      		在以前的 iOS 版本,主线程栈区 1M,子线程是 512K,而且不能修改
      	*/
      	NSUInteger stackSize = [NSThread currentThread].stackSize;
      	
      	// 设置栈区大小
      	/*
      		即 256 * 1024 = 256K,只有在当前线程中设置才有效
      	*/
      	[NSThread currentThread].stackSize = 256 * 1024;
      						
      	// 退出当前线程的执行
      	/*
      		使线程进入死亡状态,线程被终止后,后续代码都不会执行,不能在主线程中调用此方法
      		在需要手动内存管理的代码中,在终止线程之前,应该注意释放之前分配的对象
      	*/
      	[NSThread exit];
      
      	// 休眠到指定的时间
      	/*
      		使线程进入阻塞状态,线程暂时被移出可调度线程池
      	*/
      	[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:10]];
      	
      	// 休眠指定的时长
      	/*
      		使线程进入阻塞状态,线程暂时被移出可调度线程池
      	*/
      	[NSThread sleepForTimeInterval:10];
      
    • Swift

      	// 启动子线程
      	/*
      		将线程对象加入可调度线程池等待 CPU 调度,线程执行完毕后,由于内存空间已经被释放,不能再次启动
      	*/
      	myThread.start()
      
      	// 通知线程取消
      	/*
      		可以在外部终止线程执行,在线程执行方法中需要增加 isCancelled == YES,如果成立直接返回
      	*/
      	myThread.cancel()
      
      	// 获取线程名字
      	let threadName:String? = myThread.name
      
      	// 设置线程名字
      	/*
      		在多个线程开发时,可以用来判断到底是谁在执行任务
      		在大的商业项目中,通常需要在程序崩溃时,获取程序准确执行所在的线程
      	*/
      	myThread.name = "downloadImage"
      
      	// 设置线程的优先级
      	/*
      		范围 0.0 到 1.0,默认为 0.5,优先级高表示 CPU 调度的频率相对较高。开发时尽量不要修改
      	*/
      	myThread.threadPriority = 1
      
      	// 判断线程是否正在执行 readonly
      	let isExecuting:Bool = myThread.executing
      
      	// 判断线程是否完成 readonly
      	let isFinished:Bool = myThread.finished
      
      	// 判断线程是否被取消 readonly
      	let isCancelled:Bool = myThread.cancelled
      
      	// 获取当前线程
      	let currentThread:NSThread = NSThread.currentThread()
      
      	// 判断当前线程是否为主线程
      	/*
      		可以在所有的多线程技术中使用
      	*/
      	let isMainThread:Bool = NSThread.currentThread().isMainThread
      	let isMainThread:Bool = NSThread.isMainThread()
      
      	// 判断是否为多线程操作
      	let isMultiThreaded:Bool = NSThread.isMultiThreaded()
      
      	// 引用主线程
      	/*
      		返回主线程对象
      	*/
      	let mainThread:NSThread = NSThread.mainThread()
      
      	// 获取栈区大小
      	/*
      		线程执行前,主线程和子线程默认栈区大小都为 512K,线程完成后,栈区大小 0K,内存空间被释放
      		在以前的 iOS 版本,主线程栈区 1M,子线程是 512K,而且不能修改
      	*/
      	let stackSize:Int = NSThread.currentThread().stackSize
      
      	// 设置栈区大小
      	/*
      		即 256 * 1024 = 256K,只有在当前线程中设置才有效
      	*/
      	NSThread.currentThread().stackSize = 256 * 1024
      	
      	// 退出当前线程的执行
      	/*
      		使线程进入死亡状态,线程被终止后,后续代码都不会执行,不能在主线程中调用此方法
      		在需要手动内存管理的代码中,在终止线程之前,应该注意释放之前分配的对象
      	*/
      	NSThread.exit()
      
      	// 休眠到指定的时间
      	/*
      		使线程进入阻塞状态,线程暂时被移出可调度线程池
      	*/
      	NSThread.sleepUntilDate(NSDate(timeIntervalSinceNow: 10))
      
      	// 休眠指定的时长
      	/*
      		使线程进入阻塞状态,线程暂时被移出可调度线程池
      	*/
      	NSThread.sleepForTimeInterval(10)
      

    3.3 NSThread 线程间通信

    • 子线程里不允许操作 UI。

    • 在子线程中向 self 发送消息,让主线程执行某个方法。waitUntilDone: YES 等待主线程执行完成后子线程再继续,NO 主线程在执行方法的时候,子线程也同时运行。

    • Objective-C

      	// 在主线程中执行操作
      	[self performSelectorOnMainThread:@selector(updateUI:) withObject:image waitUntilDone:YES];
      
      	// 在另一个线程中执行操作
      	NSThread *mainThread = [NSThread mainThread];
      	[self performSelector:@selector(updateUI:) onThread:mainThread withObject:image waitUntilDone:YES];
      
    • Swift

      	// 在主线程中执行操作
      	self.performSelectorOnMainThread(#selector(ViewController.updateUI(_:)), withObject: image, waitUntilDone: true)
      
      	// 在另一个线程中执行操作
      	let mainThread = NSThread.mainThread()
      	self.performSelector(#selector(ViewController.updateUI(_:)), onThread: mainThread, withObject: image, waitUntilDone: true)
      

    3.4 NSThread 线程系统通知

    	NSWillBecomeMultiThreadedNotification   // 将要变成多线程
    	NSDidBecomeSingleThreadedNotification   // 已经变成单线程
    	NSThreadWillExitNotification            // 将要退出子线程
    
    • Objective-C

      	// 添加线程系统通知
      
      		[[NSNotificationCenter defaultCenter] addObserver:self 
      		                                         selector:@selector(downloadEnd:) 
      		                                             name:NSThreadWillExitNotification 
      		                                           object:nil];  
      
      	// 系统通知响应触发事件
      
          	- (void)downloadEnd:(NSNotification *)notification {
      
              	NSThread *thread = notification.object;
      
              	NSLog(@"%@ 线程结束了", thread.name);
          	}
      
    • Swift

      	// 添加线程系统通知
      
          	NSNotificationCenter.defaultCenter().addObserver(self, 
          	                                        selector: #selector(ViewController.downloadEnd(_:)), 
          	                                            name: NSThreadWillExitNotification, 
          	                                          object: nil)
          	
      	// 系统通知响应触发事件
      
      		func downloadEnd(notification: NSNotification){
      
      			let thread = notification.object as! NSThread
      
      			print("(thread.name) 线程结束了");
      		}
      

    3.5 NSThread 线程安全

    • Objective-C

      	// 对线程加锁
      
          	// 操作变量时需要加线程锁,保证同时只有一个线程在访问该变量。
      
      		// 实例化线程锁
      		NSLock *threadLock;
      		_threadLock = [[NSLock alloc] init];
      		
      		// 打开线程锁,开始对变量写操作
          	[_threadLock lock];
      
      		// 关闭线程锁,停止对变量写操作
          	[_threadLock unlock];
      
      	// 使用互斥锁
      
          	@synchronized (self) {
      
      			// 对变量写操作
        		}
      
      	// 声明变量为原子性	
      
      		// 声明变量为原子性(默认)
      		@property(assign) NSInteger page;
      
    • Swift

      	// 对线程加锁
      
      		// 操作变量时需要加线程锁,保证同时只有一个线程在访问该变量。
          
          	// 实例化线程锁
      		var threadLock:NSLock!
      		threadLock = NSLock()
      
      		// 打开线程锁,开始对变量写操作
        		threadLock.lock()
      
          	// 关闭线程锁,停止对变量写操作
       		threadLock.unlock()
      

    3.6 自旋锁&互斥锁比较

    • 如果重写了原子属性的 setter 方法,相当于覆盖了系统提供的 setter 方法,此时,系统要求,必须重写 getter 方法。

    • 定义属性时,系统默认提供 getter & setter 方法,并且生成一个 _成员变量,但是如果自己重写了 getter & setter 方法,_成员变量,不会自动生成。

    • @synthesize 合成指令,可以指定保存属性数值的 成员变量。

    • 在 Xcode 4.5 之前,开发,程序员必须自己实现 @synthesize 指令。

    • Objective-C

      	@property (atomic, strong) NSObject *obj1;
      	@property (atomic, strong) NSObject *obj2;
      
      • 原子属性模拟

        	@synthesize obj1 = _obj1;
        
        	// obj1 - getter
        
        		- (NSObject *)obj1 {
        
        			return _obj1;
        		}
        
        	// obj1 - setter
        
        		- (void)setObj1:(NSObject *)obj1 {
        
        			// 使用互斥锁
        			@synchronized(self) {
        
            			_obj1 = obj1;
        			}
        		}
        
      • 自旋锁&互斥锁性能测试

        	long largeNumber = 1000 * 1000;
        
        	// 互斥锁测试
        
        		// 2001-01-01 00:00:00 到现在的秒数
        		CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();                           	
        
        		for (int i = 0; i < largeNumber; ++i) {
        			self.obj1 = [[NSObject alloc] init];
        		}
        
        		NSLog(@"互斥锁: %f", CFAbsoluteTimeGetCurrent() - start);
        
        	// 自旋锁测试
        
        		start = CFAbsoluteTimeGetCurrent();
        
        		for (int i = 0; i < largeNumber; ++i) {
        			self.obj2 = [[NSObject alloc] init];
        		}
        
        		NSLog(@"自旋锁: %f", CFAbsoluteTimeGetCurrent() - start);
        

    4、GCD 的使用

    4.1 GCD 线程创建

    • 1、dispatch_async

      • 常用的方法 dispatch_async,为了避免界面在处理耗时的操作时卡死,比如读取网络数据,IO,数据库读写等,我们会在另外一个线程中处理这些操作,然后通知主线程更新界面。

        	dispatch_async(dispatch_get_global_queue(0, 0), ^{
        
        		// 耗时的操作 Code
        
            	dispatch_async(dispatch_get_main_queue(), ^{
        
        			// 更新界面 Code
            	});
        	});
        
    • 2、dispatch_barrier_async

      • dispatch_barrier_async 是在前面的任务执行结束后它才执行,而且它后面的任务等它执行完成之后才会执行,并且所有的任务都不能使用全局的 queue 序列。
    • 3、dispatch_group_async

      • dispatch_group_async 可以实现监听一组任务是否完成,等到 group 中的所有任务执行完毕后,由队列调度 dispatch_group_notify block 中的任务异步执行。需要在所有异步任务执行完毕后,统一获得一个通知。group 负责监控任务,queue 负责调度任务。

      • dispatch_group_async 是异步的方法,任务的执行顺序不确定,与任务添加的顺序无关。所有 dispatch_group_async 添加的任务都执行完后,再执行 dispatch_group_notify 添加的任务,但 dispatch_group_notify 添加的任务需最后添加。这个方法很有用,比如你执行两个下载任务,当两个任务都下载完成后你才通知界面说完成的了。

    • 4、dispatch_apply

      • dispatch_apply 执行某个代码片段 N 次。任务 同步执行。
    • Objective-C

      • 全局队列同步执行任务

        	// 全局队列,负责调度任务
        	dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
        
        	// 任务,使用 block 来包装任务,block 没有参数,没有返回值
        	void (^task)() = ^ {
            	
        	};
        
        	// 指定执行任务的函数,不会开启线程,就在当前线程执行 block
        	dispatch_sync(globalQueue, task);
        
      • 全局队列异步执行任务

        	// 全局队列,负责调度任务
        	dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
        
        	// 任务,使用 block 来包装任务,block 没有参数,没有返回值
        	void (^task)() = ^ {
            	
        	};
        
        	// 指定执行任务的函数,会开启线程,在其他线程执行 block
        	dispatch_async(globalQueue, task);
        
      • 创建 dispatch_async 线程

        	dispatch_async(dispatch_get_global_queue(0, 0), ^{
            
            	// 耗时的操作 Code
            	NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:imageUrlPath]];
            	UIImage *image = [[UIImage alloc] initWithData:data];
            
            	dispatch_async(dispatch_get_main_queue(), ^{
                
        			// 更新界面,刷新主线程 Code
        			self.imageView.image = image;
            	});
        	});
        
      • 顺序操作

        	// myQueue 不能使用全局的对列
        	dispatch_queue_t myQueue = dispatch_queue_create("qq", DISPATCH_QUEUE_CONCURRENT);                      
        
        	dispatch_async(myQueue, ^{
        
            	NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:imageUrlPath]];
            	_backImage = [[UIImage alloc] initWithData:data];
        	});
        
        	dispatch_async(myQueue, ^{
        
            	NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:iconImageUrlPath]];
            	_iconImage = [[UIImage alloc] initWithData:data];
        	});
        
        	dispatch_barrier_async(myQueue, ^{
        
            	dispatch_async(dispatch_get_main_queue(), ^{
        
        			self.iconImageView.image = _iconImage;
            	});	
        	});
        
        	dispatch_async(myQueue, ^{
        
            	dispatch_async(dispatch_get_main_queue(), ^{
        
        			self.imageView.image = _backImage;
            	});	
        	});
        
      • 群组操作,线程通知

        	// queue 负责调度任务
        	dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
        
        	// group 负责监控任务
        	dispatch_group_t group = dispatch_group_create();
        
        	// 第一个任务
        	dispatch_group_async(group, globalQueue, ^{
        
            	NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:imageUrlPath]];
            	_backImage = [[UIImage alloc] initWithData:data];
        	});
        
        	// 第二个任务
        	dispatch_group_async(group, globalQueue, ^{
        
            	NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:iconImageUrlPath]];
            	_iconImage = [[UIImage alloc] initWithData:data];
        	});
        
        	// 其它所有添加的任务都执行完后,再执行该任务,但该任务需最后添加
        	dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        	
            	self.imageView.image = _backImage;
            	self.iconImageView.image = _iconImage;
        	});
        
      • 群组操作实现原理

        	/*
        		The dispatch_group_async() convenience function behaves like so:
            
        		void
        		dispatch_group_async(dispatch_group_t group, dispatch_queue_t queue, dispatch_block_t block)
        		{
        			dispatch_retain(group);
        			dispatch_group_enter(group);
        			dispatch_async(queue, ^{
        				block();
        				dispatch_group_leave(group);
        				dispatch_release(group);
        			});
        		}
         	*/
        
        	// queue 负责调度任务
        	dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
        	
        	// group 负责监控任务
        	dispatch_group_t group = dispatch_group_create();
        
        	// 入组,之后的 block 会被 group 监听
        	dispatch_group_enter(group);
        	dispatch_async(globalQueue, ^{
            
            	NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:imageUrlPath]];
            	_backImage = [[UIImage alloc] initWithData:data];
        
        		// 出组,block 的末尾,所有任务执行完毕后,添加一个出组,dispatch_group_enter & dispatch_group_leave 必须成对出现
            	dispatch_group_leave(group);
        	});
        
        	// 入组,之后的 block 会被 group 监听
        	dispatch_group_enter(group);
        	dispatch_async(globalQueue, ^{
            
            	NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:iconImageUrlPath]];
            	_iconImage = [[UIImage alloc] initWithData:data];
        
        		// 出组,block 的末尾,所有任务执行完毕后,添加一个出组,dispatch_group_enter & dispatch_group_leave 必须成对出现
            	dispatch_group_leave(group);
        	});
        
        	// 阻塞式等待调度组中所有任务执行完毕
        	dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
        
        	// 群组结束
        	dispatch_group_notify(group, dispatch_get_main_queue(), ^{
            
            	self.imageView.image = _backImage;
            	self.iconImageView.image = _iconImage;	
        	});
        
      • 延迟操作

        	// 从现在开始 n 纳秒后,1.0 * NSEC_PER_SEC = 1 秒
        	dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC));
        	
        	// 延迟指定时间(纳秒)后在指定的队列中调度执行,异步执行
        	dispatch_after(when, dispatch_get_global_queue(0, 0), ^{
            
            	NSLog(@"%@", [NSThread currentThread]);
        	});
        
      • 循环操作

        	// 循环执行设定的次数(5 次),同步执行
        	dispatch_apply(5, dispatch_get_global_queue(0, 0), ^(size_t index) {                          	
            
            	NSLog(@"dispatch_apply: %zu --- %@", index, [NSThread currentThread]);
        	});
        
      • 一次性操作

        	/*
        		有的时候,在程序开发中,有些代码只想从程序启动就只执行一次,典型的应用场景就是 “单例”。
        
            	dispatch_once 能够保证 block 中的代码,只会被执行一次,onceToken == 0 时就会执行 block 的代码,执行后 变为 -1,
            	dispatch 内部也有一把锁,是能够保证 "线程安全" 的,而且是苹果公司推荐使用的。
         
            	block 同步执行,能够保证后续的代码直接使用 block 执行后的结果。
        	*/
        
        		// 同步执行
        		static dispatch_once_t onceToken;
        		dispatch_once(&onceToken, ^{
                	
        		});
        
      • 同步任务的作用

        	// 同步任务,可以让其他异步执行的任务,"依赖" 某一个同步任务。例如:在用户登录之后,再异步下载文件。
        
        	// 队列
        	dispatch_queue_t q = dispatch_queue_create("qq", DISPATCH_QUEUE_CONCURRENT);
        
        	// 异步执行 task
        	dispatch_async(q, ^{
        
        		dispatch_sync(q, ^{
            		NSLog(@"Login %@", [NSThread currentThread]);
        		});
        
        		dispatch_async(q, ^{
            		NSLog(@"Download A %@", [NSThread currentThread]);
        		});
        
        		dispatch_async(q, ^{
            		NSLog(@"Download B %@", [NSThread currentThread]);
        		});
        	});
        
      • 主队列同步任务不死锁

        	// 主线程中主队列同步执行时,主队列和主线程相互等待会造成死锁。在子线程中执行不会死锁。
        
        	// 并发队列
        	dispatch_queue_t q = dispatch_queue_create("qq", DISPATCH_QUEUE_CONCURRENT);
        
        	// 任务
        	void (^task)() = ^ {
            
            	dispatch_sync(dispatch_get_main_queue(), ^{
            		NSLog(@"come here %@", [NSThread currentThread]);
            	});
        	};
        
        	// 异步执行任务
        	dispatch_async(q, task);
        
    • Swift

      • 全局队列同步执行任务

        	// 全局队列,负责调度任务
        	let q:dispatch_queue_t = dispatch_get_global_queue(0, 0);
        
        	// 任务,使用 闭包 来包装任务,闭包 没有参数,没有返回值
        	let task:(() -> Void) = {
        	
        	}
        	
        	// 指定执行任务的函数,不会开启线程,就在当前线程执行 闭包
          	dispatch_sync(q, task);
        
      • 全局队列异步执行任务

        	// 全局队列,负责调度任务
        	let q:dispatch_queue_t = dispatch_get_global_queue(0, 0);
        
        	// 任务,使用 闭包 来包装任务,闭包 没有参数,没有返回值
        	let task:(() -> Void) = {
            	
        	}
        	
        	// 指定执行任务的函数,会开启线程,在其他线程执行 闭包
        	dispatch_async(q, task);
        
      • 创建 dispatch_async 线程

        	dispatch_async(dispatch_get_global_queue(0, 0)) {
            
            	// 耗时的操作 Code
            	
            	let data = NSData(contentsOfURL: NSURL(string: self.imageUrlPath)!)
            	let image = UIImage(data: data!)
            	
          		dispatch_async(dispatch_get_main_queue()) {
                	
                	// 更新界面,刷新主线程 Code					
            		self.imageView.image = image
            	}
        	}
        
      • 顺序操作

        	// myQueue 不能使用全局的序列
        	let myQueue:dispatch_queue_t = dispatch_queue_create("qq", DISPATCH_QUEUE_CONCURRENT)
        
        	dispatch_async(myQueue) {
            
            	let data = NSData(contentsOfURL: NSURL(string: self.imageUrlPath)!)
            	self.backImage = UIImage(data: data!)
        	}
        
        	dispatch_async(myQueue) {
        
            	let data = NSData(contentsOfURL: NSURL(string: self.iconImageUrlPath)!)
            	self.iconImage = UIImage(data: data!)
        	}
        
        	dispatch_barrier_async(myQueue) {
        
            	dispatch_async(dispatch_get_main_queue(), {
                
                	self.iconImageView.image = self.iconImage
            	})
        
            	NSThread.sleepForTimeInterval(4)
        	}
        
        	dispatch_async(myQueue) {
        
            	dispatch_async(dispatch_get_main_queue(), {
                
                	self.imageView.image = self.backImage
            	})
        	}
        
      • 群组操作

        	// queue 负责调度任务
        	let globalQueue = dispatch_get_global_queue(0, 0)
        
        	// group 负责监控任务
        	let group = dispatch_group_create()
        
        	// 第一个任务
        	dispatch_group_async(group, globalQueue) {
            
            	let data = NSData(contentsOfURL: NSURL(string: self.imageUrlPath)!)
            	self.backImage = UIImage(data: data!)
            
            	print("第一个任务完成")
        	}
        
        	// 第二个任务
        	dispatch_group_async(group, globalQueue) {
        
            	let data = NSData(contentsOfURL: NSURL(string: self.iconImageUrlPath)!)
            	self.iconImage = UIImage(data: data!)
        
            	print("第二个任务完成")
        	}
        
        	// 其它所有添加的任务都执行完后,再执行该任务,但该任务需最后添加
        	dispatch_group_notify(group, dispatch_get_main_queue()) {
        
        		// 异步执行
        	
            	self.imageView.image = self.backImage
            	self.iconImageView.image = self.iconImage
        
            	print("更新界面")
        	}
        
      • 群组操作实现原理

        	/*
            	The dispatch_group_async() convenience function behaves like so:
        
            	void
            	dispatch_group_async(dispatch_group_t group, dispatch_queue_t queue, dispatch_block_t block)
            	{
                	dispatch_retain(group);
                	dispatch_group_enter(group);
                	dispatch_async(queue, ^{
                   	block();
                   	dispatch_group_leave(group);
                   	dispatch_release(group);
                	});
            	}
        	*/
        
        	// queue 负责调度任务
        	let globalQueue = dispatch_get_global_queue(0, 0)
        	
        	// group 负责监控任务
        	let group = dispatch_group_create()
        
        	// 入组,之后的 block 会被 group 监听
        	dispatch_group_enter(group)
        	dispatch_async(globalQueue) { 
            
            	let data = NSData(contentsOfURL: NSURL(string: self.imageUrlPath)!)
            	self.backImage = UIImage(data: data!)
            	
            	// 出组,block 的末尾,所有任务执行完毕后,添加一个出,dispatch_group_enter & dispatch_group_leave 必须成对出现
            	dispatch_group_leave(group)
        	}
        
        	// 入组,之后的 block 会被 group 监听
        	dispatch_group_enter(group)
        	dispatch_async(globalQueue) {
            
            	let data = NSData(contentsOfURL: NSURL(string: self.iconImageUrlPath)!)
            	self.iconImage = UIImage(data: data!)
            	
        		// 出组,block 的末尾,所有任务执行完毕后,添加一个出组,dispatch_group_enter & dispatch_group_leave 必须成对出现
            	dispatch_group_leave(group)
        	}
        
        	// 阻塞式等待调度组中所有任务执行完毕
        	dispatch_group_wait(group, DISPATCH_TIME_FOREVER)
        
        	// 群组结束
        	dispatch_group_notify(group, dispatch_get_main_queue()) {
        
        		// 异步执行
        		
            	self.imageView.image = self.backImage
            	self.iconImageView.image = self.iconImage	
        	}
        
      • 延迟操作

        	// 从现在开始 n 纳秒后,1 * NSEC_PER_SEC = 1 秒
        	let when:dispatch_time_t = dispatch_time(DISPATCH_TIME_NOW, (Int64)(1 * NSEC_PER_SEC))
        
        	// 延迟指定时间(纳秒)后在指定的队列中调度执行,异步执行
        	dispatch_after(when, dispatch_get_global_queue(0, 0)) {
            	
        	}
        
      • 循环操作

        	// 循环执行设定的次数(5 次),同步执行
        	dispatch_apply(5, dispatch_get_global_queue(0, 0)) { (index:Int) in
            	
        	}
        
      • 一次性操作

        	/*
        		有的时候,在程序开发中,有些代码只想从程序启动就只执行一次,典型的应用场景就是 “单例”。
         
            	dispatch_once 能够保证 block 中的代码,只会被执行一次,onceToken == 0 时就会执行 block 的代码,执行后 变为 -1,
            	dispatch 内部也有一把锁,是能够保证 "线程安全" 的,而且是苹果公司推荐使用的。
         
            	block 同步执行,能够保证后续的代码直接使用 block 执行后的结果。
        	*/
        	
        	// 同步执行
        	struct Static {
        		static var onceToken: dispatch_once_t = 0
        	}
        	dispatch_once(&Static.onceToken, {
        
        	})
        
      • 同步任务的作用

        	// 同步任务,可以让其他异步执行的任务,"依赖" 某一个同步任务。例如:在用户登录之后,再异步下载文件。
        
        	// 队列
        	let q:dispatch_queue_t = dispatch_queue_create("qq", DISPATCH_QUEUE_CONCURRENT)
        
        	dispatch_async(q) { 
            
            	// 异步执行 task
            	dispatch_sync(q, {
                	
            	})
            
            	dispatch_async(q, { 
                	
            	})
            
            	dispatch_async(q, {
                	
            	})
        	}
        
      • 主队列同步任务不死锁

        	// 主线程中主队列同步执行时,主队列和主线程相互等待会造成死锁。在子线程中执行不会死锁。
        
        	// 队列
        	let q:dispatch_queue_t = dispatch_queue_create("qq", DISPATCH_QUEUE_CONCURRENT)
        
        	// 任务
        	let task:(() -> Void) = {
            
            	dispatch_async(dispatch_get_main_queue(), { 
                	
            	})
        	}
        
        	// 异步执行任务
        	dispatch_async(q, task)
        

    4.2 GCD 线程设置

    • Objective-C

      • 调度组的创建

        	// 创建调度组
        	dispatch_group_t group = dispatch_group_create();
        
      • 线程队列的创建

        	// 获取全局队列
        	dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
        
        	// 获取主线程队列
        	dispatch_queue_t mainQueue = dispatch_get_main_queue();
        
        	// 创建串行队列
        	dispatch_queue_t mySerialQueue = dispatch_queue_create("mySerialQueue", NULL);
        
        	// 创建并发队列
        	dispatch_queue_t myConcurrentQueue = dispatch_queue_create("myConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);
        
      • 全局队列的获取

        	/*
            	dispatch_queue_t dispatch_get_global_queue(long identifier, unsigned long flags);
         
            	全局队列,是 GCD 为了方便程序员的多线程开发提供的 dispatch_get_global_queue,本身就是一个并发队列。
        
            	参数:
        
                	1. identifier:服务质量(队列对任务调度的优先级)/iOS 7.0 之前,是优先级
         
                    iOS 8.0+ 告诉队列执行任务的 "服务质量 quality of service":
         
        					QOS_CLASS_USER_INTERACTIVE 0x21,               用户交互(希望尽快完成,用户对结果很期望,不要放太耗时操作)
        					QOS_CLASS_USER_INITIATED 0x19,                 用户期望(希望快,不要放太耗时操作)
        					QOS_CLASS_DEFAULT 0x15,                        默认(不是给程序员使用的,用来重置对列使用的)
        					QOS_CLASS_UTILITY 0x11,                        实用工具(耗时操作,专门用来处理耗时操作)
        					QOS_CLASS_BACKGROUND 0x09,                     后台
        					QOS_CLASS_UNSPECIFIED 0x00,                    未指定,可以和 iOS 7.0 适配
        
                    iOS 7.0 及之前 优先级:
         
        					DISPATCH_QUEUE_PRIORITY_HIGH 2                 高优先级
        					DISPATCH_QUEUE_PRIORITY_DEFAULT 0              默认优先级
        					DISPATCH_QUEUE_PRIORITY_LOW (-2)               低优先级
        					DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN   后台优先级
        
                  	提示:不要选择 BACKGROUND 的选项,苹果认为:BACKGROUND 表示用户不需要知道任务什么时候完成。选择这个选项,速度慢的令人发指!不利于调试。
        
        					关于优先级,不要搞太负责,就用最简单的。
        
                   	结论:如果要做 iOS 8.0 & iOS 7.0 的适配,使用以下代码:dispatch_get_global_queue(0, 0);
        
        					如果要做 iOS 8.0 & iOS 9.0 的适配,应该选择 QOS_CLASS_UTILITY
        
                	2. flags:保留
         
                   	标记是为了未来使用保留的。这个参数应该永远指定为 0。
        	*/
        
        	// 获取全局队列
        	dispatch_queue_t globalQueue = dispatch_get_global_queue(QOS_CLASS_UTILITY, 0);
        
    • Swift

      • 调度组的创建

        	// 创建调度组
        	let group:dispatch_group_t = dispatch_group_create()
        
      • 线程队列的创建

        	// 获取全局队列
        	let globalQueue:dispatch_queue_t = dispatch_get_global_queue(0, 0)
        
        	// 获取主线程队列
        	let mainQueue:dispatch_queue_t = dispatch_get_main_queue()
        
        	// 创建串行队列
        	let mySerialQueue:dispatch_queue_t = dispatch_queue_create("mySerialQueue", nil);
        
        	// 创建并发队列
        	let myConcurrentQueue:dispatch_queue_t = dispatch_queue_create("myConcurrentQueue", DISPATCH_QUEUE_CONCURRENT)
        

    4.3 GCD 线程间通信

    • 子线程里不允许操作 UI。

    • 在子线程中调用主线程队列,执行刷新 UI 等操作。

    • Objective-C

      	dispatch_async(dispatch_get_main_queue(), ^{
      
        		// 更新界面,刷新主线程 Code
      	});
      
    • Swift

      	dispatch_async(dispatch_get_main_queue()) {
                      
      		// 更新界面,刷新主线程 Code
      	}
      

    5、NSOperation 的使用

    5.1 NSOperation 线程创建

    • NSOpeartion 是对 GCD 的封装,是 OC 的,比 GCD 的使用简单。即将 “操作” 添加到队列。

      • 队列:全局队列(并发队列)。
      • 操作:异步执行的任务。
    • 使用 NSOperation 的方式有两种:

      • 1、用定义好的两个子类:NSInvocationOperation 和 NSBlockOperation。
      • 2、继承 NSOperation,NSOperation 也是设计用来扩展的,只需继承 NSOperation 在 .m 文件中实现 main 方法,main 方法编写要执行的代码即可。然后把 NSOperation 子类的对象放入NSOperationQueue 队列中,该队列就会启动并开始处理它。
    • Objective-C

      • 创建一个 block 风格的任务

        	// 创建任务操作队列
        	NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
        
        	NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        
            	[self downloadImage1:imageUrlPath];
        	}];
        
        	// 将一个操作任务加到队列中,如果队列中的任务数小于最大并发数,会立即执行,否则任务排队
        	[operationQueue addOperation:operation];
        
      • 直接向队列添加 block

        	// 创建任务操作队列
        	NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
        
        	// 直接向操作队列添加操作 block
        	[operationQueue addOperationWithBlock:^{
            
            	[self downloadImage1:imageUrlPath];
        	}];
        
      • 含主队列的任务

        	[[[NSOperationQueue alloc] init] addOperationWithBlock:^{
        
        		// 耗时的操作 Code
        		NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:imageUrlPath]];
        		UIImage *image = [[UIImage alloc] initWithData:data];
        
        		[[NSOperationQueue mainQueue] addOperationWithBlock:^{
            	
            		// 更新界面,刷新主线程 Code
            		self.imageView.image = image;
        		}];
        	}];
        
      • 创建一个普通的任务

        	// 创建任务操作队列
        	NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
        
        	// 创建一个普通的操作任务
        	NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self 
        	                                                                        selector:@selector(downloadImage1:) 
        	                                                                          object:imageUrlPath];
        
        	// 将一个操作任务加到队列中,如果队列中的任务数小于最大并发数,会立即执行,否则任务排队
        	[operationQueue addOperation:operation];
        
      • 子线程执行方法

        	- (void)downloadImage:(NSString *) urlPath {
        
        	}
        
    • Swift

      • 创建一个 block 风格的任务

        	// 创建任务操作队列
        	let operationQueue = NSOperationQueue()
        
        	let operation = NSBlockOperation {
        
            	self.downloadImage1(self.imageUrlPath)
        	}
        
        	// 将一个操作任务加到队列中,如果队列中的任务数小于最大并发数,会立即执行,否则任务排队
        	operationQueue.addOperation(operation)
        
      • 直接向队列添加 block

        	// 创建任务操作队列
        	let operationQueue = NSOperationQueue()
        
        	// 直接向操作队列添加操作 block
        	operationQueue.addOperationWithBlock {
            	
        		self.downloadImage1(self.imageUrlPath)
        	}
        
      • 含主队列的任务

        	NSOperationQueue().addOperationWithBlock { 
            	
            	// 耗时的操作 Code
            	let data = NSData(contentsOfURL: NSURL(string: self.imageUrlPath)!)
        
            	let image = UIImage(data: data!)
            
            	NSOperationQueue.mainQueue().addOperationWithBlock({ 
                	
                	// 更新界面,刷新主线程 Code
                	self.imageView.image = image
            	})
        	}
        
      • 子线程执行方法

        	func downloadImage(urlPath: String) {
        
        	}
        

    5.2 NSOperation 线程设置

    • Objective-C

      • 队列设置

        	// 创建全局并发队列
        	NSOperationQueue *globalQueue = [[NSOperationQueue alloc] init];
        
        	// 获取当前队列
        	NSOperationQueue *currentQueue = [NSOperationQueue currentQueue];
        
        	// 获取主队列
        	NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
        
        	// 向队列添加操作
        	[operationQueue addOperation:operation];
        
        	// 向队列添加多个操作
        	/*
        		NO 异步的,YES 同步的
        	*/
        	[operationQueue addOperations:@[operation1, operation2] waitUntilFinished:NO];
        
        	// 向队列添加操作 block
        	[operationQueue addOperationWithBlock:^{
            
        	}];
        
        	// 设置最大并发数
        	/*
        		默认情况下是 -1,-1 表示没有限制,会同时运行队列中的全部的操作
        	*/
        	operationQueue.maxConcurrentOperationCount = 4;
        
        	// 设置队列名称
        	operationQueue.name = @"myOperationQueue";
        
        	// 设置队列服务质量
        	operationQueue.qualityOfService = NSQualityOfServiceUtility;
        
        	// 设置队列是否挂起
        	/*
        		YES 挂起,NO 继续执行
        		队列挂起,当前 "没有完成的操作" 是包含在队列的操作数中的队列挂起,不会影响已经执行操作的执行状态
        		对列一旦被挂起,再添加的操作不会被调度
        	*/
        	operationQueue.suspended = YES;
        
        	// 判断队列是否挂起
        	BOOL isSuspended = operationQueue.isSuspended;
        
        	// 给队列中所有操作发送取消(cancel)消息
        	/*
        		在队列取消完成之前,操作计数并不会发生变化
        		系统的所有方法都没有对 isCancelled 做判断
        		正在执行的操作不会响应 cancel 消息,不会取消正在执行中的操作,不会影响队列的挂起状态
        	*/
        	[operationQueue cancelAllOperations];
        
        	// 获取队列中操作的数量
        	NSUInteger operationCount = operationQueue.operationCount;
        
        	// 获取队列中的所有操作
        	NSArray *operations = operationQueue.operations;
        
        	// 等待所有操作完成
        	[operationQueue waitUntilAllOperationsAreFinished];
        
      • 操作设置

        	// 设置操作间依赖关系
        	/*
        		operation2 依赖于 operation1
        	*/
        	[operation2 addDependency:operation1];
        
        	// 移除操作间依赖关系 
        	[operation2 removeDependency:operation1];
        
        	// 获取依赖的所有操作,readonly
        	NSArray *dependencies = operation2.dependencies;
        
        	// 取消操作
        	[operation cancel];
        	
        	// 判断操作是否被取消,readonly
        	BOOL isCancelled = operation.isCancelled;
        
        	// 判断操作是否正在被执行,readonly
        	BOOL isExecuting = operation.isExecuting;
        
        	// 判断操作是否执行完成,readonly
        	BOOL isFinished = operation.isFinished;
        
        	// 判断操作是否是异步执行的,readonly
        	BOOL isAsynchronous = operation.isAsynchronous;
        
        	// 判断操作是否准备好执行,readonly
        	BOOL isReady = operation.isReady;
        
        	// 设置操作名称
        	operation.name = @"myOperation";
        
        	// 设置操作队列优先级
        	operation.queuePriority = NSOperationQueuePriorityNormal;
        
        	// 设置操作服务质量
        	operation.qualityOfService = NSQualityOfServiceUtility;
        
        	// 等待操作完成
        	[operation waitUntilFinished];
        
        	// 在当前线程中开始执行操作
        	[operation start];
        
        	// 设置操作完成的回调
        	[operation setCompletionBlock:^{
            
            	NSLog(@"任务一完成了");
        	}];
        
        	// 设置操作完成的回调
        	[operationQueue.operations[0] setCompletionBlock:^{
            
            	NSLog(@"任务一完成了");
        	}];
        
    • Swift

      • 队列设置

        	// 创建全局并发队列
        	let globalQueue:NSOperationQueue = NSOperationQueue()
        
        	// 获取当前队列
        	let currentQueue:NSOperationQueue? = NSOperationQueue.currentQueue()
        
        	// 获取住队列
        	let mainQueue:NSOperationQueue = NSOperationQueue.mainQueue()
        
        	// 向队列添加操作
        	operationQueue.addOperation(operation)
        
        	// 向队列添加多个操作
        	/*
        		NO 异步的,YES 同步的
        	*/
        	operationQueue.addOperations([operation1, operation2], waitUntilFinished: false)	
        	// 向队列添加操作 block
        	operationQueue.addOperationWithBlock {
            
        	}
        
        	// 设置最大并发数
        	/*
        		默认情况下是 -1,-1 表示没有限制,会同时运行队列中的全部的操作
        	*/
        	operationQueue.maxConcurrentOperationCount = 4
        
        	// 设置队列名称
        	operationQueue.name = "myOperationQueue"
        
        	// 设置队列服务质量
        	operationQueue.qualityOfService = .Utility
        
        	// 设置队列是否挂起
        	/*
        		YES 挂起,NO 继续执行
        		队列挂起,当前 "没有完成的操作" 是包含在队列的操作数中的队列挂起,不会影响已经执行操作的执行状态
        		对列一旦被挂起,再添加的操作不会被调度
        	*/
        	operationQueue.suspended = true
        
        	// 判断队列是否挂起
        	let isSuspended:Bool = operationQueue.suspended
        
        	// 给队列中所有操作发送取消(cancel)消息
        	/*
        		在队列取消完成之前,操作计数并不会发生变化
        		系统的所有方法都没有对 isCancelled 做判断
        		正在执行的操作不回响应 cancel 消息,不会取消正在执行中的操作,不会影响队列的挂起状态
        	*/
        	operationQueue.cancelAllOperations()
        
        	// 获取队列中操作的数量
        	let operationCount:Int = operationQueue.operationCount
        
        	// 获取队列中的所有操作
        	let operations:[NSOperation] = operationQueue.operations
        
        	// 等待所有操作完成
        	operationQueue.waitUntilAllOperationsAreFinished()
        
      • 操作设置

        	// 设置操作间依赖关系
        	/*
        		operation2 依赖于 operation1
        	*/
        	operation2.addDependency(operation1)
        
        	// 移除操作间依赖关系
        	operation2.removeDependency(operation1)
        
        	// 获取依赖的所有操作,readonly
        	let dependencies:[NSOperation] = operation2.dependencies
        
        	// 取消操作
        	operation.cancel()
        
        	// 判断操作是否被取消,readonly
        	let isCancelled:Bool = operation.cancelled
        
        	// 判断操作是否正在被执行,readonly
        	let isExecuting:Bool = operation.executing
        
        	// 判断操作是否执行完成,readonly
        	let isFinished:Bool = operation.finished
        
        	// 判断操作是否是异步执行的,readonly
        	let isAsynchronous:Bool = operation.asynchronous
        
        	// 判断操作是否准备好执行,readonly
        	let isReady:Bool = operation.ready
        
        	// 设置操作名称
        	operation.name = "myOperation"
        
        	// 设置操作队列优先级
        	operation.queuePriority = .Normal
        
        	// 设置操作服务质量
        	operation.qualityOfService = .Utility
        
        	// 等待操作完成
        	operation.waitUntilFinished()
        
        	// 在当前线程中开始执行操作
        	operation.start()
        
        	// 设置操作完成的回调
        	operation.completionBlock = {
            
            	print("任务一完成了")
        	}
        
        	// 设置操作完成的回调
        	operationQueue.operations[0].completionBlock = {
            
            	print("任务一完成了")
        	}
        

    5.3 NSOperation 线程间通信

    • 子线程里不允许操作 UI

      • Objective-C

        	[[[NSOperationQueue alloc] init] addOperationWithBlock:^{                                               
            
            	// 在子队列中执行耗时的操作
        
            	[[NSOperationQueue mainQueue] addOperationWithBlock:^{                                              
                
                	// 在主队列中执行刷新 UI 等操作
            	}];
        	}];
        
      • Swift

        	NSOperationQueue().addOperationWithBlock {                                              
            
            	// 在子队列中执行耗时的操作
            
            	NSOperationQueue.mainQueue().addOperationWithBlock({                                
                
                	// 在主队列中执行刷新 UI 等操作
            	})
        	}
        

    5.4 NSOperation 指定依赖关系

    • Objective-C

      	NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
          	NSLog(@"用户登录 %@", [NSThread currentThread]);
      	}];
      
      	NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
          	NSLog(@"付费 %@", [NSThread currentThread]);
      	}];
      
      	NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
          	[NSThread sleepForTimeInterval:1.0f];
          	NSLog(@"下载 %@", [NSThread currentThread]);
      	}];
      
      	NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
          	NSLog(@"更新UI %@", [NSThread currentThread]);
      	}];
      
      	// 付费之前需要登录,在指定依赖关系时,不要出现循环依赖
      	[op2 addDependency:op1];
      
      	// 下载之前需要付费
      	[op3 addDependency:op2];
      
      	// 更新UI之前需要完成下载
      	[op4 addDependency:op3];
      
      	// NO 异步的,YES 同步的
      	[[[NSOperationQueue alloc] init] addOperations:@[op1, op2, op3] waitUntilFinished:NO];
      
      	// 更新 UI 的操作,应该由主队列来调度
      	[[NSOperationQueue mainQueue] addOperation:op4];
      
    • Swift

      	let op1 = NSBlockOperation.init { 
          	print("用户登录 (NSThread.currentThread())")
      	}
      
      	let op2 = NSBlockOperation.init {
          	print("付费 (NSThread.currentThread())")
      	}
      
      	let op3 = NSBlockOperation.init {
          	print("下载 (NSThread.currentThread())")
      	}
      
      	let op4 = NSBlockOperation.init {
          	print("更新UI (NSThread.currentThread())")
      	}
      
      	// 付费之前需要登录,在指定依赖关系时,不要出现循环依赖
      	op2.addDependency(op1)
      
      	// 下载之前需要付费
      	op3.addDependency(op2)
      
      	// 更新UI之前需要完成下载
      	op4.addDependency(op3)
      
      	// NO 异步的,YES 同步的
      	NSOperationQueue().addOperations([op1, op2, op3], waitUntilFinished: false)
      
      	// 更新 UI 的操作,应该由主队列来调度
      	NSOperationQueue.mainQueue().addOperation(op4)
      

    5.5 自定义 NSOperation 操作

    • Objective-C

      • WebImageOperation.h

        	@interface WebImageOperation : NSOperation
        
        	///  实例化 web 图像操作
        	+ (instancetype)webImageOperationWithURLString:(NSString *)urlString 
        	                                    completion:(void (^)(UIImage *image))completion;
        
        	@end
        
      • WebImageOperation.m

        	/// 下载图片的 URL
        	@property (nonatomic, copy) NSString *urlStr;
        
        	/// 下载完成的回调
        	@property (nonatomic, copy) void (^completion) (UIImage *image);
        
        	+ (instancetype)webImageOperationWithURLString:(NSString *)urlString completion:(void (^)(UIImage *))completion {
            
            	WebImageOperation *imageOperation = [[self alloc] init];
            
            	imageOperation.urlStr= urlString;
            	imageOperation.completion = completion;
            
            	return imageOperation;
        	}
        
        	// 操作加入队列后会自动执行该方法
        	- (void)main {
            	@autoreleasepool {
                	
                	if (self.isCancelled) return;
                
                	NSURL *url = [NSURL URLWithString:self.urlStr];
                	NSData *data = [NSData dataWithContentsOfURL:url];
                
                	if (self.isCancelled) return;
                
                	if (self.completion && data != nil) {
                    
        				[[NSOperationQueue mainQueue] addOperationWithBlock:^{
                        
        					self.completion([UIImage imageWithData:data]);	
        				}];
                	}
            	}
        	}
        
      • ViewController.m

        	// 自定义 NSOperation 操作
        	WebImageOperation *downloadOperation = [WebImageOperation webImageOperationWithURLString:imageUrlPath 
        	                                                                              completion:^(UIImage *image) {
            	
            	self.imageView.image = image;
        	}];
        
        	// 将操作添加到队列
        	[[[NSOperationQueue alloc] init] addOperation:downloadOperation];
        
    • Swift

      • WebImageOperation.swift

        	class WebImageOperation: NSOperation
        
        	/// 下载图片的 URL
        	var urlStr:String!
        
        	/// 下载完成的回调
        	var completion:((image:UIImage) -> Void)!
        
        	class func webImageOperationWithURLString(urlString:String, 
        	                               completion:((image:UIImage) -> Void)) -> WebImageOperation {
            
            	let imageOperation:WebImageOperation = WebImageOperation()
            
            	imageOperation.urlStr = urlString
            	imageOperation.completion = completion
            
            	return imageOperation
        	}
        
        	// 操作加入队列后会自动执行该方法
        	override func main() {
            	
            	if self.cancelled == true {
                	return
            	}
            
            	let url:NSURL = NSURL(string: self.urlStr)!
            	let data:NSData? = NSData(contentsOfURL: url)
            
            	if self.cancelled == true {
                	return
            	}
            
            	if (self.completion != nil) && (data != nil) {
                
                	NSOperationQueue.mainQueue().addOperationWithBlock({
                    
        				self.completion(image: UIImage(data: data!)!)
                	})
            	}
        	}
        
      • ViewController.swift

        	// 自定义 NSOperation 操作
        	let downloadOperation:WebImageOperation = WebImageOperation.webImageOperationWithURLString(imageUrlPath) 
        	                                                           { (image:UIImage) in
        	
        		self.imageView.image = image
        	}
        
        	// 将操作添加到队列
        	NSOperationQueue().addOperation(downloadOperation)
        
  • 相关阅读:
    IE block my cookie in iframe
    error app/styles/components/iconfont.scss (Line 12: Invalid GBK character "xE5")
    angular4开发过程中遇到的问题和知识点记录
    博客
    009android初级篇之APP中使用系统相机相册等集成应用
    012android初级篇之Handler机制
    android studio win7开发环境
    001windows已遇到一个关键性问题 一分钟后自动重启
    008android初级篇之jni中数组的传递
    006android初级篇之jni数据类型映射
  • 原文地址:https://www.cnblogs.com/QianChia/p/5770470.html
Copyright © 2011-2022 走看看