4.3 多线程进阶篇<中>(GCD)
本文源码 Demo 详见 Github
https://github.com/shorfng/iOS-4.0-multithreading.git
- 纯C语言,提供了非常多强大的函数
- GCD是苹果公司为多核的并行运算提出的解决方案
- GCD会自动利用更多的CPU内核(比如双核、四核)
- GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
- 程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码
- 确定想做的事情
- GCD会自动将队列中的任务取出,放到对应的线程中执行
- 任务的取出遵循队列的FIFO原则:先进先出,后进后出
- 同步
- 异步
()任务的执行:同步 //queue:队列 block:任务
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block); ()任务的执行:异步 - 函数1 //queue:队列 block:任务
dispatch_async(dispatch_queue_t queue, dispatch_block_t block); - 函数2 // 在前面的任务执行结束后它才执行,而且它后面的任务等它执行完成之后才会执行
dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
- 同步:只能在当前线程中执行任务,不具备开启新线程的能力
- 异步:可以在新的线程中执行任务,具备开启新线程的能力
- 并发:允许多个任务并发(同时)执行
- 串行:一个任务执行完毕后,再执行下一个任务
- 可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)
- 并发功能只有在异步(dispatch_async)函数下才有效
- 让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)
- 凡是函数名种带有create\copy\new\retain等字眼, 都应该在不需要使用这个数据的时候进行release
- GCD的数据类型在ARC环境下不需要再做release dispatch_release(queue); // 非ARC需要释放手动创建的队列
- CF(Core Foundation)的数据类型在ARC环境下还是需要再做release

| (1)如果异步任务前面有同步任务 就会先执行同步任务同步任务是按顺序执行的任务等他执行完了才会执行并行中的异步任务 (可以做到阻塞 控制任务的执行顺序) |
| (2)如果异步任务后面有同步任务 两个任务会并行(同时)执行 |
方式1 - 手动创建并发队列: dispatch_queue_create(
constchar *label, // 队列名称
dispatch_queue_attr_t attr // 队列的类型
); // 1.创建并发队列 DISPATCH_QUEUE_CONCURRENT (并发)
dispatch_queue_t queue = dispatch_queue_create(“TD", DISPATCH_QUEUE_CONCURRENT); // 2.非ARC需要释放手动创建的队列
dispatch_release(queue); 方式2 - 获取全局并发队列:(GCD默认已经提供了全局的并发队列,供整个应用使用,可以无需手动创建) Xcode .2定义方式:
dispatch_get_global_queue(
long identifier, // 队列的优先级
unsignedlong flags // 此参数暂时无用,用0即可
); 全局并发队列的优先级:(级别由高到低) #define DISPATCH_QUEUE_PRIORITY_HIGH 2 // 高
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // 默认(中)
#define DISPATCH_QUEUE_PRIORITY_LOW (-2) // 低
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN // 后台 // 获取全局并发队列
dispatch_queue_t queue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, );
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self syncConcurrent];
}
#pragma mark - 同步函数 + 并发队列:不会开启新的线程,在当前线程执行任务(主线程),顺序执行,并发队列失去了并发的功能
- (void)syncConcurrent {
NSLog(@"同步并发 ----- begin");
// 1.获得全局的并发队列
dispatch_queue_t queue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, );
// 2.将任务加入队列
dispatch_sync(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3-----%@", [NSThread currentThread]);
});
NSLog(@"同步并发 ----- end");
}
#pragma mark - 写法2
- (void)concurrentSync {
// 1. 创建并发队列
dispatch_queue_t conCurrentQueue =
dispatch_queue_create("TD", DISPATCH_QUEUE_CONCURRENT);
// 2. 创建任务
void (^task1)() = ^() {
NSLog(@"---task1---%@", [NSThread currentThread]);
};
void (^task2)() = ^() {
NSLog(@"---task2---%@", [NSThread currentThread]);
};
void (^task3)() = ^() {
NSLog(@"---task3---%@", [NSThread currentThread]);
};
// 3. 将同步任务添加到并发队列中
dispatch_sync(conCurrentQueue, task1);
dispatch_sync(conCurrentQueue, task2);
dispatch_sync(conCurrentQueue, task3);
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
打印结果:
-- ::08.387 同步并发[:] 同步并发 ----- begin
-- ::08.387 同步并发[:] -----<NSThread: 0x7fe963d07360>{number = , name = main}
-- ::08.387 同步并发[:] -----<NSThread: 0x7fe963d07360>{number = , name = main}
-- ::08.388 同步并发[:] -----<NSThread: 0x7fe963d07360>{number = , name = main}
-- ::08.388 同步并发[:] 同步并发 ----- end
-- ::07.968 同步并发[:] ---task1---<NSThread: 0x7f8e71400d20>{number = , name = main}
-- ::07.969 同步并发[:] ---task2---<NSThread: 0x7f8e71400d20>{number = , name = main}
-- ::07.969 同步并发[:] ---task3---<NSThread: 0x7f8e71400d20>{number = , name = main}
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self asyncConcurrent];
}
#pragma mark - 异步函数 + 并发队列:可以同时开启多条线程,在当前线程执行任务(主线程),无序执行(按照任务添加到队列中的顺序被调度),线程条数具体由`可调度线程池/底层线程池`来决定
- (void)asyncConcurrent {
NSLog(@"异步并发 ----- begin");
// 1.获得全局的并发队列
dispatch_queue_t queue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, );
// 2.将任务加入队列
dispatch_sync(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3-----%@", [NSThread currentThread]);
});
NSLog(@"异步并发 ----- begin");
}
#pragma mark - 写法2
- (void)concurrentAsync {
// 1.创建并发队列
dispatch_queue_t conCurrentQueue =
dispatch_queue_create("TD", DISPATCH_QUEUE_CONCURRENT);
// 2. 创建任务
void (^task1)() = ^() {
NSLog(@"---task1---%@", [NSThread currentThread]);
};
void (^task2)() = ^() {
NSLog(@"---task2---%@", [NSThread currentThread]);
};
void (^task3)() = ^() {
NSLog(@"---task3---%@", [NSThread currentThread]);
};
// 3. 将异步任务添加到并发队列中
dispatch_async(conCurrentQueue, task1);
dispatch_async(conCurrentQueue, task2);
dispatch_async(conCurrentQueue, task3);
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
打印结果:
-- ::16.307 异步并发[:] 异步并发 ----- begin
-- ::16.308 异步并发[:] -----<NSThread: 0x7fd598d02490>{number = , name = main}
-- ::16.308 异步并发[:] -----<NSThread: 0x7fd598d02490>{number = , name = main}
-- ::16.308 异步并发[:] -----<NSThread: 0x7fd598d02490>{number = , name = main}
-- ::16.308 异步并发[:] 异步并发 ----- begin
-- ::18.557 异步并发[:] ---task2---<NSThread: 0x7fbf68d927b0>{number = , name = (null)}
-- ::18.557 异步并发[:] ---task3---<NSThread: 0x7fbf68e24570>{number = , name = (null)}
-- ::18.557 异步并发[:] ---task1---<NSThread: 0x7fbf68f15ae0>{number = , name = (null)}
2.2.2 创建队列 - 串行队列
手动创建串行队列: dispatch_queue_create(
constchar *label, // 队列名称
dispatch_queue_attr_t attr // 队列的类型
); //1.创建串行队列 //方式1:DISPATCH_QUEUE_SERIAL (串行)
dispatch_queue_t queue = dispatch_queue_create(“TD", DISPATCH_QUEUE_SERIAL);
//方式2:传 NULL
dispatch_queue_t queue = dispatch_queue_create(“TD", NULL); // 2.非ARC需要释放手动创建的队列
dispatch_release(queue);
- 串行队列中的任务都是按顺序执行,谁在前就先执行谁
- 主线程和子线程平等,一样谁在前选执行谁
- 执行完一个才会执行下一个任务
- 串行队列中的任务都是按顺序执行,谁在前就先执行谁
- 主线程和子线程平等,一样谁在前选执行谁
- 执行完一个才会执行下一个任务
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self syncSerial];
}
#pragma mark - 同步函数 + 串行队列:不会开启新的线程,在当前线程执行任务(主线程),任务是串行的(顺序执行)
- (void)syncSerial {
NSLog(@"同步串行 ----- begin");
// 1.创建串行队列
dispatch_queue_t queue = dispatch_queue_create("TD", DISPATCH_QUEUE_SERIAL);
// 2.将任务加入队列
dispatch_sync(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3-----%@", [NSThread currentThread]);
});
NSLog(@"同步串行 ----- end");
}
#pragma mark - 写法2
- (void)serialSyncDemo {
// 1.创建队列
dispatch_queue_t serialQueue =
dispatch_queue_create("TD", DISPATCH_QUEUE_SERIAL);
// 2.创建任务
void (^task1)() = ^() {
NSLog(@"task1---%@", [NSThread currentThread]);
};
void (^task2)() = ^() {
NSLog(@"task2---%@", [NSThread currentThread]);
};
void (^task3)() = ^() {
NSLog(@"task3---%@", [NSThread currentThread]);
};
// 3.将同步任务,添加到串行队列
dispatch_sync(serialQueue, task3);
dispatch_sync(serialQueue, task1);
dispatch_sync(serialQueue, task2);
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
打印结果:
-- ::13.648 同步串行[:] 同步串行 ----- begin
-- ::13.649 同步串行[:] -----<NSThread: 0x7fab52f04910>{number = , name = main}
-- ::13.649 同步串行[:] -----<NSThread: 0x7fab52f04910>{number = , name = main}
-- ::13.649 同步串行[:] -----<NSThread: 0x7fab52f04910>{number = , name = main}
-- ::13.649 同步串行[:] 同步串行 ----- end
-- ::53.272 同步串行[:] task1---<NSThread: 0x7fd910c05150>{number = , name = main}
-- ::53.273 同步串行[:] task2---<NSThread: 0x7fd910c05150>{number = , name = main}
-- ::53.273 同步串行[:] task3---<NSThread: 0x7fd910c05150>{number = , name = main}
- 串行队列中的任务都是按顺序执行,谁在前就先执行谁
- 主线程和子线程平等,一样谁在前选执行谁
- 执行完一个才会执行下一个任务
|
串行队列,异步任务,在多线程中,是斯坦福大学最推荐的一种多线程方式!
|
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self asyncSerial];
}
#pragma mark - 异步函数 + 串行队列:会开启新的线程,在子线程执行任务,任务是串行的(顺序执行),只开一条线程
- (void)asyncSerial {
NSLog(@"异步串行 ----- begin");
NSLog(@"主线程 ----- %@", [NSThread mainThread]);
// 1.创建串行队列
//写法1:
dispatch_queue_t queue = dispatch_queue_create("TD", DISPATCH_QUEUE_SERIAL);
//写法2:
dispatch_queue_t queue = dispatch_queue_create("TD", NULL);
// 2.将任务加入队列
dispatch_async(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3-----%@", [NSThread currentThread]);
});
NSLog(@"异步串行 ----- end");
}
#pragma mark - 写法2
- (void)serialAsyncDemo {
// 1.创建队列
dispatch_queue_t serialQueue =
dispatch_queue_create("TD", DISPATCH_QUEUE_SERIAL);
// 2.创建任务
void (^task1)() = ^() {
NSLog(@"task1---%@", [NSThread currentThread]);
};
void (^task2)() = ^() {
NSLog(@"task2---%@", [NSThread currentThread]);
};
void (^task3)() = ^() {
NSLog(@"task3---%@", [NSThread currentThread]);
};
// 3.将异步任务添加到串行队列
dispatch_async(serialQueue, task1);
dispatch_async(serialQueue, task2);
dispatch_async(serialQueue, task3);
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
打印结果:
-- ::38.392 异步串行[:] 异步串行 ----- begin
-- ::38.393 异步串行[:] 主线程 ----- <NSThread: 0x7ff271701ba0>{number = , name = main}
-- ::38.393 异步串行[:] 异步串行 ----- end
-- ::38.393 异步串行[:] -----<NSThread: 0x7ff2717abb30>{number = , name = (null)}
-- ::38.394 异步串行[:] -----<NSThread: 0x7ff2717abb30>{number = , name = (null)}
-- ::38.394 异步串行[:] -----<NSThread: 0x7ff2717abb30>{number = , name = (null)}
-- ::21.844 异步串行[:] task1---<NSThread: 0x7fddb9405f40>{number = , name = (null)}
-- ::21.845 异步串行[:] task2---<NSThread: 0x7fddb9405f40>{number = , name = (null)}
-- ::21.845 异步串行[:] task3---<NSThread: 0x7fddb9405f40>{number = , name = (null)}
2.2.3 创建队列 - 主队列(特殊的串行队列)
|
获取主队列: dispatch_get_main_queue(void); dispatch_queue_t queue = dispatch_get_main_queue();
2.2.3【代码】同步 + 主队列
主队列中不能用同步任务,无论是在异步任务前还是后都会死锁
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self syncMain];
}
#pragma mark - 同步函数 + 主队列:不会开启新的线程,会出现"死等",可能导致`主线程`卡死
- (void)syncMain {
NSLog(@"同步主队列 ----- begin");
// 1.获得主队列
dispatch_queue_t queue = dispatch_get_main_queue();
// 2.将任务加入队列
dispatch_sync(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3-----%@", [NSThread currentThread]);
});
NSLog(@"同步主队列 ----- end");
}
- (void)mainQueueSync {
NSLog(@"同步主队列 ----- begin");
// 1.获取主队列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
// 2.创建队列
void (^task1)() = ^() {
NSLog(@"---task1---%@", [NSThread currentThread]);
};
void (^task2)() = ^() {
NSLog(@"---task2---%@", [NSThread currentThread]);
};
void (^task3)() = ^() {
NSLog(@"---task3---%@", [NSThread currentThread]);
};
// 3.将同步任务添加到并发队列中
dispatch_sync(mainQueue, task1);
dispatch_sync(mainQueue, task2);
dispatch_sync(mainQueue, task3);
NSLog(@"同步主队列 ----- end");
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
打印结果:
-- ::55.594 同步主队列[:] 同步主队列 ----- begin
2.3【代码】异步 + 主队列
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self asyncMain];
}
#pragma mark - 异步函数 + 主队列:不会开启新的线程,在当前线程执行任务(主线程),任务是串行的(顺序执行),只开一条线程(适合处理 UI 或者是 UI事件)
- (void)asyncMain {
NSLog(@"异步主队列 ----- begin");
// 1.获得主队列
dispatch_queue_t queue = dispatch_get_main_queue();
// 2.将任务加入队列
dispatch_async(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3-----%@", [NSThread currentThread]);
});
NSLog(@"异步主队列 ----- end");
}
#pragma mark - 写法2
- (void)mainQueueAsync {
NSLog(@"异步主队列 ----- begin");
// 1.获取主队列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
// 2.创建任务
void (^task1)() = ^() {
NSLog(@"---async task1---%@", [NSThread currentThread]);
};
void (^task2)() = ^() {
NSLog(@"---async task2---%@", [NSThread currentThread]);
};
void (^task3)() = ^() {
NSLog(@"---async task3---%@", [NSThread currentThread]);
};
// 3.将异步任务添加到主队列中
dispatch_async(mainQueue, task1);
dispatch_async(mainQueue, task2);
dispatch_async(mainQueue, task3);
NSLog(@"异步主队列 ----- end");
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
打印结果:
-- ::47.663 异步主队列[:] 异步主队列 ----- begin
-- ::47.663 异步主队列[:] 异步主队列 ----- end
-- ::47.664 异步主队列[:] -----<NSThread: 0x7feec9c082e0>{number = , name = main}
-- ::47.664 异步主队列[:] -----<NSThread: 0x7feec9c082e0>{number = , name = main}
-- ::47.664 异步主队列[:] -----<NSThread: 0x7feec9c082e0>{number = , name = main}
-- ::15.690 异步主队列[:] 异步主队列 ----- begin
-- ::15.691 异步主队列[:] 异步主队列 ----- end
-- ::15.691 异步主队列[:] ---async task1---<NSThread: 0x7f9de1c074e0>{number = , name = main}
-- ::15.691 异步主队列[:] ---async task2---<NSThread: 0x7f9de1c074e0>{number = , name = main}
-- ::15.692 异步主队列[:] ---async task3---<NSThread: 0x7f9de1c074e0>{number = , name = main}
2.2.4 总结
GCD 队列类型的创建方式:
- 并发队列:手动创建、全局
- 串行队列:手动创建、主队列

|
(1)并发队列:不会开线程
(2)串行队列:不会开线程
|
|
(1)并发队列:能开启N条线程
(2)串行队列:开启1条线程
|
(1)
| (1)如果异步任务前面有同步任务 就会先执行同步任务同步任务是按顺序执行的任务等他执行完了才会执行并行中的异步任务 (可以做到阻塞 控制任务的执行顺序) |
| (2)如果异步任务后面有同步任务 两个任务会并行(同时)执行 |
(3)
#import "ViewController.h" @interface ViewController ()
@property(weak, nonatomic) IBOutlet UIImageView *imageView;
@end @implementation ViewController - (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
} - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { // 全局的异步并发
dispatch_async(
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{ // 图片的网络路径
NSURL *url = [NSURL
URLWithString:@"http://www.5068.com/u/faceimg/20140804114111.jpg"]; // 加载图片
NSData *data = [NSData dataWithContentsOfURL:url]; // 生成图片
UIImage *image = [UIImage imageWithData:data]; // 回到主线程,执行 UI 刷新操作
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = image;
});
});
} - (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
} @end
图片下载示例
4.1 其他用法 - barrier函数
#import "ViewController.h" @interface ViewController ()
@end @implementation ViewController - (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
} - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { // 这里使用全局并发队列的方式会导致 dispatch_barrier_async 功能失效
dispatch_queue_t queue =
dispatch_queue_create("TD", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{
NSLog(@"----1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----2-----%@", [NSThread currentThread]);
}); dispatch_barrier_async(queue, ^{
NSLog(@"----barrier-----%@", [NSThread currentThread]);
}); dispatch_async(queue, ^{
NSLog(@"----3-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----4-----%@", [NSThread currentThread]);
});
} - (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
} @end 打印结果: -- ::16.214 其他用法 - barrier函数[:] ---------<NSThread: 0x7fc4a0c2c5d0>{number = , name = (null)}
-- ::16.214 其他用法 - barrier函数[:] ---------<NSThread: 0x7fc4a0f0cb20>{number = , name = (null)}
-- ::16.214 其他用法 - barrier函数[:] ----barrier-----<NSThread: 0x7fc4a0f0cb20>{number = , name = (null)}
-- ::16.215 其他用法 - barrier函数[:] ---------<NSThread: 0x7fc4a0f0cb20>{number = , name = (null)}
-- ::16.215 其他用法 - barrier函数[:] ---------<NSThread: 0x7fc4a0c2c5d0>{number = , name = (null)}
4.2 其他用法 - 延迟执行
方法1:调用NSObject的方法 // 该方法在那个线程调用,那么run就在哪个线程执行(当前线程),通常是主线程
// 2秒后再调用self的run方法
[selfperformSelector:@selector(run) withObject:nilafterDelay:2.0]; 方法2:使用GCD函数 // 这里是在主线程执行,如果想要在子线程执行,选择相应的队列
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 2秒后异步执行这里的代码...
}); 方法3:使用NSTimer [NSTimer scheduledTimerWithTimeInterval:2.0
target:self
selector:@selector(run)
userInfo:nil
repeats:NO];
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self delay1];
}
#pragma mark - 方法1:调用NSObject的方法
- (void)delay1 {
NSLog(@"touchesBegan-----");
// 2秒后再调用self的run方法
[self performSelector:@selector(run) withObject:nil afterDelay:2.0];
}
#pragma mark - 方法2:使用 GCD 函数
- (void)delay2 {
NSLog(@"touchesBegan-----");
// 这里是在主线程执行,如果想要在子线程执行,选择相应的队列
dispatch_after(
dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)),
dispatch_get_main_queue(), ^{
NSLog(@"run-----");
});
}
#pragma mark - 方法3:使用NSTimer定时器
- (void)delay3 {
NSLog(@"touchesBegan-----");
[NSTimer scheduledTimerWithTimeInterval:2.0
target:self
selector:@selector(run)
userInfo:nil
repeats:NO];
}
- (void)run {
NSLog(@"run-----");
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
打印结果:
-- ::56.384 其他用法 - 延迟执行[:] touchesBegan-----
-- ::58.385 其他用法 - 延迟执行[:] run-----
4.3 其他用法 - 一次性代码
使用dispatch_once 函数能保证某段代码在程序运行过程中只被执行1次,适合做资源的加载 static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 只执行1次的代码(这里面默认是线程安全的)
});
}
4.4 其他用法 - 快速迭代
使用dispatch_apply函数能进行快速迭代遍历:
dispatch_apply(<#size_t iterations#>, <#dispatch_queue_t queue#>, ^(size_t) {
// 代码
});
dispatch_apply(, dispatch_get_global_queue(, ), ^(size_t index){
// 执行10次代码,index顺序不确定
});
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self apply];
}
#pragma mark - 文件剪切方法1:快速迭代
- (void)apply {
dispatch_queue_t queue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, );
NSString *from = @"/Users/TD/Desktop/From";
NSString *to = @"/Users/TD/Desktop/To";
NSFileManager *mgr = [NSFileManager defaultManager];
NSArray *subpaths = [mgr subpathsAtPath:from];
dispatch_apply(subpaths.count, queue, ^(size_t index) {
NSString *subpath = subpaths[index];
NSString *fromFullpath = [from stringByAppendingPathComponent:subpath];
NSString *toFullpath = [to stringByAppendingPathComponent:subpath];
// 剪切
[mgr moveItemAtPath:fromFullpath toPath:toFullpath error:nil];
NSLog(@"%@---%@", [NSThread currentThread], subpath);
});
}
#pragma mark - 文件剪切方法2:传统方式
- (void)moveFile {
NSString *from = @"/Users/TD/Desktop/From";
NSString *to = @"/Users/TD/Desktop/To";
NSFileManager *mgr = [NSFileManager defaultManager];
//获取文件夹下的所有文件路径,包括子文件夹下的文件路径
NSArray *subpaths = [mgr subpathsAtPath:from];
for (NSString *subpath in subpaths) {
//全路径
NSString *fromFullpath = [from stringByAppendingPathComponent:subpath];
NSString *toFullpath = [to stringByAppendingPathComponent:subpath];
dispatch_async(
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{
// 剪切
[mgr moveItemAtPath:fromFullpath toPath:toFullpath error:nil];
});
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
文件剪切示例
4.5 其他用法 - 队列组/调度组
// 创建一个队列组
dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{
// 执行1个耗时的异步操作
}); dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{
// 执行1个耗时的异步操作
}); dispatch_group_notify(group, dispatch_get_main_queue(),
^{
// 等前面的异步操作都执行完毕后,回到主线程...
});
#import "ViewController.h" @interface ViewController ()
@property(weak, nonatomic) IBOutlet UIImageView *imageView;
@property(nonatomic, strong) UIImage *image1; //图片1
@property(nonatomic, strong) UIImage *image2; //图片2
@end @implementation ViewController - (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
} - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { dispatch_queue_t queue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ); // 创建一个队列组
dispatch_group_t group = dispatch_group_create(); // 1.下载图片1
dispatch_group_async(group, queue, ^{
// 图片的网络路径
NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
// 加载图片
NSData *data = [NSData dataWithContentsOfURL:url];
// 生成图片
self.image1 = [UIImage imageWithData:data];
}); // 2.下载图片2
dispatch_group_async(group, queue, ^{
// 图片的网络路径
NSURL *url = [NSURL URLWithString: @"http://pic38.nipic.com/20140228/5571398_215900721128_2.jpg"];
// 加载图片
NSData *data = [NSData dataWithContentsOfURL:url];
// 生成图片
self.image2 = [UIImage imageWithData:data];
}); // 3.将图片1、图片2合成一张新的图片(也可以直接在此处回到主线程,只不过是因为绘制图片比较耗时,没有放在主线程而已)
dispatch_group_notify(group, queue, ^{ // 开启新的图形上下文
UIGraphicsBeginImageContext(CGSizeMake(, )); // 绘制图片
[self.image1 drawInRect:CGRectMake(, , , )];
[self.image2 drawInRect:CGRectMake(, , , )]; // 取得上下文中的图片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); // 结束上下文
UIGraphicsEndImageContext(); // 回到主线程显示图片
dispatch_async(dispatch_get_main_queue(), ^{
// 4.将新图片显示出来
self.imageView.image = image;
});
});
} - (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
} @end
图片下载后合成示例
大致概况如下:

5.0 GCD 的定时器事件
- CFRunLoopTimerRef 基本上说的就是 NSTimer,它受 RunLoop 的 Mode 影响
- GCD 的定时器不受 RunLoop 的 Mode 影响
代码示例:当滚动文字的时候,是不会影响 GCD的定时器的
#import "ViewController.h" @interface ViewController ()
// 定时器 (这里不用带*,因为dispatch_source_t就是个类,内部已经包含了*)
@property(nonatomic, strong) dispatch_source_t timer;
@end @implementation ViewController - (void)viewDidLoad {
[super viewDidLoad];
} int count = ;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { // 1、获得队列
// dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_queue_t queue = dispatch_get_main_queue(); // 2、创建一个定时器 (dispatch_source_t本质还是个OC对象)
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, , , queue); // 3、设置定时器的各种属性(几时开始任务,每隔多长时间执行一次) // 触发时间(何时开始执行第一个任务)
// 比当前时间晚1秒
dispatch_time_t start =
dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC));
// 马上就执行
// dispatch_time_t start1 = DISPATCH_TIME_NOW; // 时间间隔。GCD的时间参数,一般是纳秒(1秒 == 10的9次方纳秒)
uint64_t interval = (uint64_t)(1.0 * NSEC_PER_SEC); // 参数:(1)定时器名字 (2)触发时间 (3)时间间隔 (4)0
dispatch_source_set_timer(self.timer, start, interval, ); // 4、设置定时器的回调
dispatch_source_set_event_handler(self.timer, ^{
NSLog(@"------------%@", [NSThread currentThread]); count++; if (count == ) {
// 取消定时器
dispatch_cancel(self.timer);
self.timer = nil;
}
}); // 5、启动定时器
dispatch_resume(self.timer);
}
@end 打印结果: -- ::39.066 -掌握-GCD定时器[:] ------------<NSThread: 0x7f8af0705770>{number = , name = main}
-- ::40.067 -掌握-GCD定时器[:] ------------<NSThread: 0x7f8af0705770>{number = , name = main}
-- ::41.066 -掌握-GCD定时器[:] ------------<NSThread: 0x7f8af0705770>{number = , name = main}
-- ::42.067 -掌握-GCD定时器[:] ------------<NSThread: 0x7f8af0705770>{number = , name = main}
4.3 多线程进阶篇<中>(GCD)的更多相关文章
- 4.4 多线程进阶篇<下>(NSOperation)
本文并非最终版本,如有更新或更正会第一时间置顶,联系方式详见文末 如果觉得本文内容过长,请前往本人"简书" 本文源码 Demo 详见 Github https://github.c ...
- 4.1/4.2 多线程进阶篇<上>(Pthread & NSThread)
本文并非最终版本,如有更新或更正会第一时间置顶,联系方式详见文末 如果觉得本文内容过长,请前往本人 “简书” 本文源码 Demo 详见 Githubhttps://github.com/shorfng ...
- ASP.NET MVC URL重写与优化(进阶篇)-继承RouteBase玩转URL
http://www.cnblogs.com/John-Connor/archive/2012/05/03/2478821.html 引言-- 在初级篇中,我们介绍了如何利用基于ASP.NET MVC ...
- [转载]ASP.NET MVC URL重写与优化(进阶篇)-继承RouteBase玩转URL
引言-- 在初级篇中,我们介绍了如何利用基于ASP.NET MVC的Web程序中的Global文件来简单的重写路由.也介绍了它本身的局限性-依赖于路由信息中的键值对: 如果键值对中没有的值,我们无法将 ...
- ASP.NET MVC URL重写与优化(进阶篇)-继承RouteBase
原文地址:http://www.51csharp.com/MVC/882.html ASP.NET MVC URL重写与优化(进阶篇)-继承RouteBase玩转URL 引言-- 在初级篇中,我们 ...
- Spring+SpringMVC+MyBatis整合进阶篇(四)RESTful实战(前端代码修改)
前言 前文<RESTful API实战笔记(接口设计及Java后端实现)>中介绍了RESTful中后端开发的实现,主要是接口地址修改和返回数据的格式及规范的修改,本文则简单介绍一下,RES ...
- Java进阶篇(六)——Swing程序设计(上)
Swing是GUI(图形用户界面)开发工具包,内容有很多,这里会分块编写,但在进阶篇中只编写Swing中的基本要素,包括容器.组件和布局等,更深入的内容会在高级篇中出现.想深入学习的朋友们可查阅有关资 ...
- [转]ASP.NET MVC URL重写与优化(进阶篇)-继承RouteBase玩转URL
本文转自:http://www.cnblogs.com/John-Connor/archive/2012/05/03/2478821.html 引言-- 在初级篇中,我们介绍了如何利用基于ASP.NE ...
- springcloud-zuul进阶篇
一 前言 经过zuul初级篇(博客或者公主号springcloud专栏可以找到)的学习,读者都懂得如何简单的使用zuul进行路由网关配置,在进阶篇中你将获得zuul核心功能过滤器的基本使用,通过zuu ...
随机推荐
- JSON数据的使用
JSON (JavaScript Object Notation)一种简单的数据格式,比xml更轻巧. JSON 是 JavaScript 原生格式,这意味着在 JavaScript 中处理 JSON ...
- android:clipChildren属性的作用
该属性默认为true,这个属性需要添加到最顶层的ViewGroup,作用是控制子View是否可以超出它所在的父View设定的边界 比如ImageView设置高度100dp,而它所在的父View设置的高 ...
- 父类div高度适应子类div
父类div高度适应子类div 通常有许多div的高度由子类的高度决定父类的高度,所以需要父类div要适应子类div的高度,一般情况父类的高度可以直接设置成“auto”即可. 在有的情况下,子类div会 ...
- xpath定位中starts-with、contains和text()的用法
starts-with 顾名思义,匹配一个属性开始位置的关键字 contains 匹配一个属性值中包含的字符串 text() 匹配的是显示文本信息,此处也可以用来做定位用 eg //input[sta ...
- NSIS对话框单位造成的控件移位问题
在使用NSIS脚本开发安装卸载程序,使用自定义的nsdialog控件.发现在小部分系统上安装时,一些控件会消失,或者挪位.于是排除问题,看看这些控件的为位置和坐标,发现基本上是使用了对话框单位的控件, ...
- VS Build目录下各文件的作用
VS2010中各种类型文件的作用: .sln 相当于VC6中 .dsw .vcxproj 相当于VC6中 .dsp .suo 相当于VC6中 .ncb .vcxproj.filter ...
- 那些PHP中没有全称的简写
PHP中的GD库,全网没发现GD二字母的全称是什么,包括PHP.net,都搜不到GD.G应该是graphi,D是什么? die: 从php_mysql.dll到php_mysqli的变化,那个i是什么 ...
- C# Stream
转载:C# 温故而知新:Stream篇(一.二) http://www.cnblogs.com/JimmyZheng/archive/2012/03/17/2402814.html#no2 http: ...
- UIWebView获取网页点击事件
//接收web事件 -(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request nav ...
- 表达式求值(noip2015等价表达式)
题目大意 给一个含字母a的表达式,求n个选项中表达式跟一开始那个等价的有哪些 做法 模拟一个多项式显然难以实现那么我们高兴的找一些素数代入表达式,再随便找一个素数做模表达式求值优先级表 - ( ) + ...