进程是指在系统中正在运行的一个应用程序。每一个进程之间是独立的,每一个进程均运行在其专用且受保护的内存空间内。ios
好比同时打开 QQ、Xcode,系统就会分别启动两个进程。经过 “活动监视器” 能够查看 Mac 系统中所开启的进程。程序员
一个程序的一次运行,在执行过程当中拥有独立的内存单元,而多个线程共享一块内存。web
线程是进程中执行任务的基本执行单元。一个进程要执行任务,必须得有线程,一个进程(程序)的全部任务都在线程中执行。每个进程至少有一条线程,即主线程。一个进程能够开启多条线程,每条线程能够并发(同时)执行不一样的任务。数据库
好比使用酷狗播放音乐、使用迅雷下载电影,都须要在线程中执行。swift
在程序中每个方法的执行,都是从上向下串行执行的。除非使用 block,不然在一个方法中,全部代码的执行都在同一个线程上。安全
进程与线程的联系:线程是进程的基本组成单位。服务器
一个线程中任务的执行是串行(顺序执行)的。若是要在一个线程中执行多个任务,那么只能一个一个的按顺序执行这些任务,也就是说,在同一时间内,一个线程只能执行一个任务。网络
好比在一个线程中下载三个文件(分别是文件 A,文件 B,文件 C)。多线程
同一时间,CPU 只能处理一条线程,只有一条线程在工做(执行)。多线程并发(同时)执行,实际上是 CPU 快速的在多条线程之间调度(切换)。若是 CPU 调度线程的时间足够快,就形成了多条线程并发执行的假象。闭包
多线程开发的复杂度相对较高,在开发时能够按照如下套路编写代码:
在多线程开发的时候,有几点提示:
线程越多,程序设计就更加复杂,好比线程之间的通讯、多线程的数据共享就更加复杂。
一般开启 5 到 6 条线程便可,不能开启的太多。
一个 iOS 程序运行后,默认会开启一条线程,称为主线程或 UI 线程。主线程主要用于显示/刷新 UI 界面,处理 UI 事件(好比点击事件、滚动事件、拖拽事件等)。
注意别将比较耗时的操做放到主线程中,耗时的操做会卡住主线程,严重影响 UI 的流畅度,给用户一种 “卡” 的坏体验。
新线程建立启动后,线程对象被加入可调度线程池中,进入就绪状态,等待 CPU 调度。在线程被调度运行时,若是线程调用了 sleep 方法或者等待同步锁时,线程由运行状态进入到阻塞状态,线程被移出可调度线程池。sleep 到时或者获得同步锁时,线程从新进入可调度线程池,回到就绪状态。线程任务执行完毕或者异常、强制退出时,线程由运行状态进入到死亡状态,线程结束,线程占用内存空间被释放。
要实现线程安全,就必需要用到锁,用到锁就会下降程序的性能。为了获得更佳的用户体验,UIKit 不是线程安全的,UI 线程约定全部更新 UI 的操做都必须主线程上执行,所以,主线程又被称为 UI 线程。
1)iOS 开发建议:
2)使用互斥锁:
@synchronized (self) { }
在 iOS 开发中,使用锁的机会极少,通常都是从服务器获取到数据,直接显示。
3)使用变量原子性:
原子属性,解决不了卖票问题,由于卖票的读写都须要锁定。
定义属性时,系统默认提供 getter & setter 方法,而且生成一个 _成员变量,可是若是本身重写了 getter & setter 方法,_成员变量,不会自动生成。
在 Xcode 4.5 以前,开发,程序员必须本身实现 @synthesize 指令。
4)自旋锁 & 互斥锁:
在子线程中执行比较耗时的操做(以下载图片等),子线程执行完毕后通知主线程更新 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]; }];
pthread 是 POSIX 的多线程开发框架,因为是跨平台的 C 语言框架,在苹果的头文件中并无详细的注释。Xcode 中所用跨平台的文件一般都在 usr/include 目录下。对于全部跨平台的框架(如:pthread & socket),能够访问一个网站: baike.baidu.com
iOS 中 C 语言框架:
一般,在 C 语言框架中,“对象” 类型以 _t/Ref 结尾,并且定义时不须要使用 * 。
内存管理:
参数桥接:
void *:
C 语言中的 void * 和 OC 中的 id 是等价的。
例如:
C : void *(*)(void *) OC : id (函数名) (id) 即 返回值类型(函数名)(参数) block : 返回值 (^) (参数) block 匿名的函数指针
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 没有参数也没有返回值。
2) 执行任务的函数:
异步 dispatch_async
同步 dispatch_sync
在当前线程执行 block 的任务。
同步任务的做用:同步任务,可让其余异步执行的任务,"依赖" 某一个同步任务。例如:在用户登陆以后,再异步下载文件!
3) 任务调度队列:
GCD 中的 FIFO 队列称为 dispatch queue(调度队列)。系统提供了许多预约义的 dispatch queue,包括能够保证始终在主线程上执行工做的 dispatch queue。也能够建立本身的 dispatch queue,并且能够建立任意多个。GCD 的 dispatch queue 严格遵循 FIFO(先进先出) 原则,添加到 dispatch queue 的工做单元将始终按照加入 dispatch queue 的顺序启动。dispatch queue 按先进先出的顺序,串行或并发地调度任务在线程上实行。
dispatch queue 分为下面三种:
Serial:
Concurrent:
并发队列,又称为 global dispatch queues,一次能够 "调度" 多个任务,尽量多地启动任务并发执行,任务执行完成的顺序是随机的。
系统给每个应用程序提供了三个 concurrent dispatch queues。这三个并发调度队列是全局的,它们只有优先级的不一样。由于是全局的,咱们不须要去建立。咱们只须要经过使用函数 dispath_get_global_queue 去获得队列。
Main dispatch queue:
4) 各类队列的执行效果:
Type | 全局并发队列 | 手动建立串行队列 | 主队列
-----------------|--------------------|--------------------|-------------------
同步 (sync) | 没有开启新线程 | 没有开启新线程 | 没有开启新线程
~ | 串行执行任务 | 串行执行任务 | 串行执行任务
异步 (async) | 有开启新线程 | 有开启新线程 | 没有开启新线程
~ | 并发执行任务 | 串行执行任务 | 串行执行任务
开不开线程由执行任务的函数决定:
开几条线程由队列决定:
并发队列开多条线程。
主队列不会开启线程。
5) 队列的选择:
多线程的目的:将耗时的操做放在后台执行!
串行队列,只开一条线程,全部任务顺序执行
并发队列,会开启多条线程,全部任务不按照顺序执行
实际开发中
6) 全局队列 & 并发队列的区别:
全局队列:
并发队列:
7) GCD & NSThread 对比:
NSOperation 也是苹果公司推出的 “并发” 技术。是基于 OC 语言的,iOS 2.0 推出。GCD 推出以后,苹果对 NSOperation 底层从新编写过,是对 GCD 的封装。Cocoa operation 相关的类是 NSOperation,NSOperationQueue。NSOperation 是个抽象类,使用它必须用它的子类,能够实现它或者使用它定义好的两个子类:NSInvocationOperation 和 NSBlockOperation。
优势:不须要关心线程管理,数据同步的事情,能够把精力放在本身须要执行的操做上。
1)NSOperation 与 GCD 对比:
NSOperation:
核心概念:把 "操做(异步执行的任务)" 添加到队列(全局的并发队列)。即建立 NSOperation 子类的对象,把对象添加到 NSOperationQueue 队列里执行。
OC 的框架,更加面向对象,是对 GCD 的封装
GCD:
核心概念:将 "任务(block)" 添加到队列(串行/并发/全局/主队列),而且指定任务执行的函数(同步/异步)
C 语言的框架,dispatch_xxx 函数
2)自定义 NSOperation 操做流程:
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; }
- (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) { }
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)
在子线程中向 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)
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) 线程结束了"); }
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()
定义属性时,系统默认提供 getter & setter 方法,而且生成一个 _成员变量,可是若是本身重写了 getter & setter 方法,_成员变量,不会自动生成。
在 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);
一、dispatch_async
经常使用的方法 dispatch_async,为了不界面在处理耗时的操做时卡死,好比读取网络数据,IO,数据库读写等,咱们会在另一个线程中处理这些操做,而后通知主线程更新界面。
dispatch_async(dispatch_get_global_queue(0, 0), ^{ // 耗时的操做 Code dispatch_async(dispatch_get_main_queue(), ^{ // 更新界面 Code }); });
二、dispatch_barrier_async
三、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 添加的任务需最后添加。这个方法颇有用,好比你执行两个下载任务,当两个任务都下载完成后你才通知界面说完成的了。
四、dispatch_apply
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)
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)
在子线程中调用主线程队列,执行刷新 UI 等操做。
Objective-C
dispatch_async(dispatch_get_main_queue(), ^{ // 更新界面,刷新主线程 Code });
Swift
dispatch_async(dispatch_get_main_queue()) { // 更新界面,刷新主线程 Code }
NSOpeartion 是对 GCD 的封装,是 OC 的,比 GCD 的使用简单。即将 “操做” 添加到队列。
使用 NSOperation 的方式有两种:
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) { }
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("任务一完成了") }
子线程里不容许操做 UI
Objective-C
[[[NSOperationQueue alloc] init] addOperationWithBlock:^{ // 在子队列中执行耗时的操做 [[NSOperationQueue mainQueue] addOperationWithBlock:^{ // 在主队列中执行刷新 UI 等操做 }]; }];
Swift
NSOperationQueue().addOperationWithBlock { // 在子队列中执行耗时的操做 NSOperationQueue.mainQueue().addOperationWithBlock({ // 在主队列中执行刷新 UI 等操做 }) }
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)
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)