系统进程
AOP(切面编程)
Aspects
MultipleThread
Thread
Pthreads
POSIX 线程(POSIX threads),简称 Pthreads,是线程的 POSIX 标准。该标准定义了创建和操纵线程的一整套 API。在类 Unix 操作系统(Unix、Linux、Mac OS X 等)中,都使用 Pthreads 作为操作系统的线程。
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
pthread_t thread;
//创建一个线程并自动执行
pthread_create(&thread, NULL, start, NULL);
}
void *start(void *data) {
NSLog(@"%@", [NSThread currentThread]);
return NULL;
}
NSThread
这套方案是经过苹果封装后的,并且完全面向对象的。所以你可以直接操控线程对象,非常直观和方便。但是,它的生命周期还是需要我们手动管理,所以这套方案也是偶尔用用,比如 [NSThread currentThread],它可以获取当前线程类,你就可以知道当前线程的各种属性,用于调试十分方便。下面来看看它的一些用法。
- 先创建线程类,再启动
OBJECTIVE-C
` ``// 创建`` ``NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:nil];`` ``// 启动`` ``[thread start];`
SWIFT
` ``//创建`` ``let thread = NSThread(target: self, selector: ``"run:"``, object: nil)`` ``//启动`` ``thread.start()`
- 创建并自动启动
OBJECTIVE-C
` ``[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:nil];`
SWIFT
` ``NSThread.detachNewThreadSelector(``"run:"``, toTarget: self, withObject: nil)`
常见方法
//取消线程
- (void)cancel;
//启动线程
- (void)start;
//判断某个线程的状态的属性
@property (readonly, getter=isExecuting) BOOL executing;
@property (readonly, getter=isFinished) BOOL finished;
@property (readonly, getter=isCancelled) BOOL cancelled;
//设置和获取线程名字
-(void)setName:(NSString *)n;
-(NSString *)name;
//获取当前线程信息
+ (NSThread *)currentThread;
//获取主线程信息
+ (NSThread *)mainThread;
//使当前线程暂停一段时间,或者暂停到某个时刻
+ (void)sleepForTimeInterval:(NSTimeInterval)time;
+ (void)sleepUntilDate:(NSDate *)date;
线程同步
线程跳转
我们都知道在其他线程操作完成后必须到主线程更新 UI。所以,我们来看看有哪些方法可以回到主线程。
Asynchronous
PromiseKit
Concurrence
GCD
Grand Central Dispatch,听名字就霸气。它是苹果为多核的并行运算提出的解决方案,所以会自动合理地利用更多的 CPU 内核(比如双核、四核),最重要的是它会自动管理线程的生命周期(创建线程、调度任务、销毁线程),完全不需要我们管理。
队列获取与创建
- 主队列:特殊的串行队列
这是一个特殊的 串行队列。什么是主队列,大家都知道吧,它用于刷新 UI,任何需要刷新 UI 的工作都要在主队列执行,所以一般耗时的任务都要放到别的线程执行。
//OBJECTIVE-C
dispatch_queue_t queue = dispatch_get_main_queue();
//SWIFT
let queue = dispatch_get_main_queue()
- 全局并行队列
这应该是唯一一个并行队列,只要是并行任务一般都加入到这个队列。
//OBJECTIVE-C
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//SWIFT
let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
- 自建队列
自己可以创建 串行队列, 也可以创建 并行队列。看下面的代码(代码已更新),它有两个参数,第一个上面已经说了,第二个才是最重要的。第二个参数用来表示创建的队列是串行的还是并行的,传入 DISPATCH_QUEUE_SERIAL 或 NULL 表示创建串行队列。传入 DISPATCH_QUEUE_CONCURRENT 表示创建并行队列。
//OBJECTIVE-C
dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", NULL);
//SWIFT
let queue = dispatch_queue_create("tk.bourne.testQueue", nil);
任务
- 同步任务:不会新开线程
//OJC
dispatch_sync(, ^{
//code here
NSLog(@"%@", [NSThread currentThread]);
});
//Swift
dispatch_sync(, { () -> Void in
//code here
println(NSThread.currentThread())
})
- 异步任务:会新开线程
//OJC
dispatch_sync(, ^{
//code here
NSLog(@"%@", [NSThread currentThread]);
});
//Swift
dispatch_sync(, { () -> Void in
//code here
println(NSThread.currentThread())
})
Async:GCD 的语法糖
Cocoa 官方提供的这一套 Callback 方式的 GCD 的写法还是有些恶心,有人对其进行了一些封装,使得 GCD 的使用更加的方便,可以参考Async:
Async.background {
println("This is run on the background queue")
}.main {
println("This is run on the main queue, after the previous block")
}
在 CocoaPods 中使用:
use_frameworks!
pod "AsyncSwift"
即可以引入 (1)Supports the modern queue classes:
Async.main {}
Async.userInteractive {}
Async.userInitiated {}
Async.utility {}
Async.background {}
(2)Chain as many blocks as you want:
Async.userInitiated {
// 1
}.main {
// 2
}.background {
// 3
}.main {
// 4
}
(3)Store reference for later chaining:
let backgroundBlock = Async.background {
print("This is run on the background queue")
}
// Run other code here...
// Chain to reference
backgroundBlock.main {
print("This is run on the \(qos_class_self().description) (expected \(qos_class_main().description)), after the previous block")
}
(4)Custom queues:
let customQueue = dispatch_queue_create("CustomQueueLabel", DISPATCH_QUEUE_CONCURRENT)
let otherCustomQueue = dispatch_queue_create("OtherCustomQueueLabel", DISPATCH_QUEUE_CONCURRENT)
Async.customQueue(customQueue) {
print("Custom queue")
}.customQueue(otherCustomQueue) {
print("Other custom queue")
}
(5)Dispatch block after delay:
let seconds = 0.5
Async.main(after: seconds) {
print("Is called after 0.5 seconds")
}.background(after: 0.4) {
print("At least 0.4 seconds after previous block, and 0.9 after Async code is called")
}
(6)Cancel blocks that aren’t already dispatched:
// Cancel blocks not yet dispatched
let block1 = Async.background {
// Heavy work
for i in 0...1000 {
print("A \(i)")
}
}
let block2 = block1.background {
print("B – shouldn't be reached, since cancelled")
}
Async.main {
// Cancel async to allow block1 to begin
block1.cancel() // First block is _not_ cancelled
block2.cancel() // Second block _is_ cancelled
}
(7)Wait for block to finish – an ease way to continue on current queue after background task:
let block = Async.background {
// Do stuff
}
// Do other stuff
block.wait()
AsyncGroup
Multiple dispatch blocks with GCD:
let group = AsyncGroup()
group.background {
// Run on background queue
}
group.utility {
// Run on utility queue, in parallel to the previous block
}
group.wait()
All modern queue classes:
group.main {}
group.userInteractive {}
group.userInitiated {}
group.utility {}
group.background {}
Custom queues:
let customQueue = dispatch_queue_create("Label", DISPATCH_QUEUE_CONCURRENT)
group.customQueue(customQueue) {}
Wait for group to finish:
let group = AsyncGroup()
group.background {
// Do stuff
}
group.background {
// Do other stuff in parallel
}
// Wait for both to finish
group.wait()
// Do rest of stuff
Custom asynchronous operations:
let group = AsyncGroup()
group.enter()
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
// Do stuff
group.leave()
}
group.enter()
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
// Do other stuff in parallel
group.leave()
}
// Wait for both to finish
group.wait()
// Do rest of stuff
队列组
队列组可以将很多队列添加到一个组里,这样做的好处是,当这个组里所有的任务都执行完了,队列组会通过一个方法通知我们。下面是使用方法,这是一个很实用的功能。
//1.创建队列组
dispatch_group_t group = dispatch_group_create();
//2.创建队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//3.多次使用队列组的方法执行任务, 只有异步方法
//3.1.执行3次循环
dispatch_group_async(group, queue, ^{
for (NSInteger i = 0; i < 3; i++) {
NSLog(@"group-01 - %@", [NSThread currentThread]);
}
});
//3.2.主队列执行8次循环
dispatch_group_async(group, dispatch_get_main_queue(), ^{
for (NSInteger i = 0; i < 8; i++) {
NSLog(@"group-02 - %@", [NSThread currentThread]);
}
});
//3.3.执行5次循环
dispatch_group_async(group, queue, ^{
for (NSInteger i = 0; i < 5; i++) {
NSLog(@"group-03 - %@", [NSThread currentThread]);
}
});
//4.都完成后会自动通知
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"完成 - %@", [NSThread currentThread]);
});
NSOperation
GCD 是基于 c 的底层 api,NSOperation 属于 object-c 类。ios 首先引入的是 NSOperation,IOS4 之后引入了 GCD 和 NSOperationQueue 并且其内部是用 gcd 实现的。相对于 GCD:
1,NSOperation 拥有更多的函数可用,具体查看 api。
2,在 NSOperationQueue 中,可以建立各个 NSOperation 之间的依赖关系。
3,有 kvo,可以监测 operation 是否正在执行(isExecuted)、是否结束(isFinished),是否取消(isCanceld)。
4,NSOperationQueue 可以方便的管理并发、NSOperation 之间的优先级。
NSOperation 只是一个抽象类,所以不能封装任务。但它有 2 个子类用于封装任务。分别是:NSInvocationOperation 和 NSBlockOperation。创建一个 Operation 后,需要调用 start 方法来启动任务,它会 默认在当前队列同步执行。当然你也可以在中途取消一个任务,只需要调用其 cancel 方法即可。不过因为 NSInvocationOperation 不是类型安全的,其在 Swift 中已经被弃用。
创建任务
//1.创建NSBlockOperation对象
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%@", [NSThread currentThread]);
}];
//2.开始任务
[operation start];
之前说过这样的任务,默认会在当前线程执行。但是 NSBlockOperation 还有一个方法:addExecutionBlock:,通过这个方法可以给 Operation 添加多个执行 Block。这样 Operation 中的任务 会并发执行,它会 在主线程和其它的多个线程 执行这些任务,注意下面的打印结果:
//1.创建NSBlockOperation对象
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%@", [NSThread currentThread]);
}];
//添加多个Block
for (NSInteger i = 0; i < 5; i++) {
[operation addExecutionBlock:^{
NSLog(@"第%ld次:%@", i, [NSThread currentThread]);
}];
}
//2.开始任务
[operation start];
打印结果:
2015-07-28 17:50:16.585 test[17527:4095467] 第2次 -{number = 1, name = main}
2015-07-28 17:50:16.585 test[17527:4095666] 第1次 -{number = 4, name = (``null``)}
2015-07-28 17:50:16.585 test[17527:4095665]{number = 3, name = (``null``)}
2015-07-28 17:50:16.585 test[17527:4095662] 第0次 -{number = 2, name = (``null``)}
2015-07-28 17:50:16.586 test[17527:4095666] 第3次 -{number = 4, name = (``null``)}
2015-07-28 17:50:16.586 test[17527:4095467] 第4次 -{number = 1, name = main}
创建队列
看过上面的内容就知道,我们可以调用一个 NSOperation 对象的 start() 方法来启动这个任务,但是这样做他们默认是 同步执行 的。就算是 addExecutionBlock 方法,也会在 当前线程和其他线程 中执行,也就是说还是会占用当前线程。这是就要用到队列 NSOperationQueue 了。而且,按类型来说的话一共有两种类型:主队列、其他队列。只要添加到队列,会自动调用任务的 start() 方法。
- 主线程
//OBJECTIVE-C
NSOperationQueue *queue = [NSOperationQueue mainQueue];
//SWIFT
let queue = NSOperationQueue.mainQueue()
- 其他队列
因为主队列比较特殊,所以会单独有一个类方法来获得主队列。那么通过初始化产生的队列就是其他队列了,因为只有这两种队列,除了主队列,其他队列就不需要名字了。注意:其他队列的任务会在其他线程并行执行。
//1.创建一个其他队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//2.创建NSBlockOperation对象
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%@", [NSThread currentThread]);
}];
//3.添加多个Block
for (NSInteger i = 0; i < 5; i++) {
[operation addExecutionBlock:^{
NSLog(@"第%ld次:%@", i, [NSThread currentThread]);
}];
}
//4.队列添加任务
[queue addOperation:operation];
任务依赖
NSOperation 有一个非常实用的功能,那就是添加依赖。比如有 3 个任务:A: 从服务器上下载一张图片,B:给这张图片加个水印,C:把图片返回给服务器。这时就可以用到依赖了:
//1.任务一:下载图片
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"下载图片 - %@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0];
}];
//2.任务二:打水印
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"打水印 - %@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0];
}];
//3.任务三:上传图片
NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"上传图片 - %@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0];
}];
//4.设置依赖
[operation2 addDependency:operation1]; //任务二依赖任务一
[operation3 addDependency:operation2]; //任务三依赖任务二
//5.创建队列并加入任务
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperations:@[operation3, operation2, operation1] waitUntilFinished:NO];