GCD延时操作怎么凯富k资产停止释放并释放

3520人阅读
iOS 知识点(13)
1.定时器的创建与实用
_mytimer =& [NSTimer scheduledTimerWithTimeInterval:time target:self selector:@selector(refresh) userInfo:nil repeats:YES];
注:repeats:参数表示是否循环使用定时器,NO表示只调用一次。另外,将计数器的repeats设置为YES的时候,self的引用计数会加1。因此可能会导致self(即viewController)不能release,所以,必须在viewWillAppear的时候,将计数器timer停止,否则可能会导致内存泄露。
2.定时器的关闭。
& [_mytimer invalidate];
&&& _mytimer=
& 一定要记得赋值为空,这样才是真的释放。
3.有时候我们需要定时器的暂停与开始,代码如下:
& - (void)viewWillAppear:(BOOL)animated {
&&& //启动定时器
&&& [_mytimer setFireDate:[NSDate distantPast]];
-(void)viewWillDisappear:(BOOL)animated
&&& [_mytimer setFireDate:[NSDate distantFuture]];
但是NSTimer有如下弊端:
必须保证有一个活跃的runloop,子线程的runloop是默认关闭的。这时如果不手动激活runloop,performSelector和scheduledTimerWithTimeInterval的调用将是无效的NSTimer的创建与撤销必须在同一个线程操作、performSelector的创建与撤销必须在同一个线程操作。内存管理有潜在泄露的风险
如果不是通过invalidate来关闭是无法停止的。还有持有self,造成对象无法释放。
所以我还是比较推荐用dispatch的timer
#import &ViewController.h&
@interface ViewController ()
dispatch_source_t _
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.view.backgroundColor = [UIColor redColor];
[self startGCDTimer];
-(void) startGCDTimer{
NSTimeInterval period = 1.0; //设置时间间隔
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
dispatch_source_set_timer(_timer, dispatch_walltime(NULL, 0), period * NSEC_PER_SEC, 0); //每秒执行
dispatch_source_set_event_handler(_timer, ^{
//在这里执行事件
NSLog(@&每秒执行test&);
dispatch_resume(_timer);
-(void) pauseTimer{
if(_timer){
dispatch_suspend(_timer);
-(void) resumeTimer{
if(_timer){
dispatch_resume(_timer);
-(void) stopTimer{
if(_timer){
dispatch_source_cancel(_timer);
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:26006次
排名:千里之外
原创:23篇
(3)(4)(3)(5)(10)(6)
(window.slotbydup = window.slotbydup || []).push({
id: '4740887',
container: s,
size: '250,250',
display: 'inlay-fix'博客分类:
GCD(Grand Central Dispatch)可以说是Mac、iOS开发中的一大“利器”,本文就总结一些有关使用GCD的经验与技巧。
dispatch_once_t必须是全局或static变量
这一条算是“老生常谈”了,但我认为还是有必要强调一次,毕竟非全局或非static的dispatch_once_t变量在使用时会导致非常不好排查的bug,正确的如下:
static dispatch_once_t onceT
dispatch_once(&onceToken, ^{
其实就是保证dispatch_once_t只有一份实例。
dispatch_queue_create的第二个参数
dispatch_queue_create,创建队列用的,它的参数只有两个,原型如下:
dispatch_queue_t dispatch_queue_create ( const char *label, dispatch_queue_attr_t attr );
在网上的大部分教程里(甚至Apple自己的文档里),都是这么创建串行队列的:
dispatch_queue_t queue = dispatch_queue_create("com.example.MyQueue", NULL);
看,第二个参数传的是“NULL”。 但是dispatch_queue_attr_t类型是有已经定义好的常量的,所以我认为,为了更加的清晰、严谨,最好如下创建队列:
dispatch_queue_t queue = dispatch_queue_create("com.example.MyQueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue = dispatch_queue_create("com.example.MyQueue", DISPATCH_QUEUE_CONCURRENT);
常量就是为了使代码更加“易懂”,更加清晰,既然有,为啥不用呢~
dispatch_after是延迟提交,不是延迟运行
先看看官方文档的说明:
Enqueue a block for execution at the specified time.
Enqueue,就是入队,指的就是将一个Block在特定的延时以后,加入到指定的队列中,不是在特定的时间后立即运行!。
看看如下代码示例:
dispatch_queue_t queue = dispatch_queue_create("me.tutuge.test.gcd", DISPATCH_QUEUE_SERIAL);
NSLog(@"Begin add block...");
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:10];
NSLog(@"First block done...");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), queue, ^{
NSLog(@"After...");
结果如下:
20:57:27.122 GCDTest[6] Begin add block...
20:57:37.127 GCDTest[1] First block done...
20:57:37.127 GCDTest[1] After...
从结果也验证了,dispatch_after只是延时提交block,并不是延时后立即执行。所以想用dispatch_after精确控制运行状态的朋友可要注意了~
正确创建dispatch_time_t
用dispatch_after的时候就会用到dispatch_time_t变量,但是如何创建合适的时间呢?答案就是用dispatch_time函数,其原型如下:
dispatch_time_t dispatch_time ( dispatch_time_t when, int64_t delta );
第一个参数一般是DISPATCH_TIME_NOW,表示从现在开始。
那么第二个参数就是真正的延时的具体时间。
这里要特别注意的是,delta参数是“纳秒!”,就是说,延时1秒的话,delta应该是“”=。=,太长了,所以理所当然系统提供了常量,如下:
#define NSEC_PER_SEC ull
#define USEC_PER_SEC 1000000ull
#define NSEC_PER_USEC 1000ull
关键词解释:
NSEC:纳秒。
USEC:微妙。
NSEC_PER_SEC,每秒有多少纳秒。
USEC_PER_SEC,每秒有多少毫秒。(注意是指在纳秒的基础上)
NSEC_PER_USEC,每毫秒有多少纳秒。
所以,延时1秒可以写成如下几种:
dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC);
dispatch_time(DISPATCH_TIME_NOW, 1000 * USEC_PER_SEC);
dispatch_time(DISPATCH_TIME_NOW, USEC_PER_SEC * NSEC_PER_USEC);
最后一个“USEC_PER_SEC * NSEC_PER_USEC”,翻译过来就是“每秒的毫秒数乘以每毫秒的纳秒数”,也就是“每秒的纳秒数”,所以,延时500毫秒之类的,也就不难了吧~
dispatch_suspend != 立即停止队列的运行
dispatch_suspend,dispatch_resume提供了“挂起、恢复”队列的功能,简单来说,就是可以暂停、恢复队列上的任务。但是这里的“挂起”,并不能保证可以立即停止队列上正在运行的block,看如下例子:
dispatch_queue_t queue = dispatch_queue_create("me.tutuge.test.gcd", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:5];
NSLog(@"After 5 seconds...");
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:5];
NSLog(@"After 5 seconds again...");
NSLog(@"sleep 1 second...");
[NSThread sleepForTimeInterval:1];
NSLog(@"suspend...");
dispatch_suspend(queue);
NSLog(@"sleep 10 second...");
[NSThread sleepForTimeInterval:10];
NSLog(@"resume...");
dispatch_resume(queue);
运行结果如下:
00:32:09.903 GCDTest[4] sleep 1 second...
00:32:10.910 GCDTest[4] suspend...
00:32:10.910 GCDTest[4] sleep 10 second...
00:32:14.908 GCDTest[6] After 5 seconds...
00:32:20.911 GCDTest[4] resume...
00:32:25.912 GCDTest[6] After 5 seconds again...
可知,在dispatch_suspend挂起队列后,第一个block还是在运行,并且正常输出。
结合文档,我们可以得知,dispatch_suspend并不会立即暂停正在运行的block,而是在当前block执行完成后,暂停后续的block执行。
所以下次想暂停正在队列上运行的block时,还是不要用dispatch_suspend了吧~
“同步”的dispatch_apply
dispatch_apply的作用是在一个队列(串行或并行)上“运行”多次block,其实就是简化了用循环去向队列依次添加block任务。但是我个人觉得这个函数就是个“坑”,先看看如下代码运行结果:
dispatch_queue_t queue = dispatch_queue_create("me.tutuge.test.gcd", DISPATCH_QUEUE_SERIAL);
dispatch_apply(3, queue, ^(size_t i) {
NSLog(@"apply loop: %zu", i);
NSLog(@"After apply");
运行的结果是:
00:55:40.854 GCDTest[9] apply loop: 0
00:55:40.856 GCDTest[9] apply loop: 1
00:55:40.856 GCDTest[9] apply loop: 2
00:55:40.856 GCDTest[9] After apply
看,明明是提交到异步的队列去运行,但是“After apply”居然在apply后打印,也就是说,dispatch_apply将外面的线程(main线程)“阻塞”了!
查看官方文档,dispatch_apply确实会“等待”其所有的循环运行完毕才往下执行=。=,看来要小心使用了。
避免死锁!
dispatch_sync导致的死锁
涉及到多线程的时候,不可避免的就会有“死锁”这个问题,在使用GCD时,往往一不小心,就可能造成死锁,看看下面的“死锁”例子:
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"I am block...");
你可能会说,这么低级的错误,我怎么会犯,那么,看看下面的:
- (void)updateUI1 {
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"Update ui 1");
[self updateUI2];
- (void)updateUI2 {
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"Update ui 2");
在你不注意的时候,嵌套调用可能就会造成死锁!所以为了“世界和平”=。=,我们还是少用dispatch_sync吧。
dispatch_apply导致的死锁!
啥,dispatch_apply导致的死锁?。。。是的,前一节讲到,dispatch_apply会等循环执行完成,这不就差不多是阻塞了吗。看如下例子:
dispatch_queue_t queue = dispatch_queue_create("me.tutuge.test.gcd", DISPATCH_QUEUE_SERIAL);
dispatch_apply(3, queue, ^(size_t i) {
NSLog(@"apply loop: %zu", i);
dispatch_apply(3, queue, ^(size_t j) {
NSLog(@"apply loop inside %zu", j);
这端代码只会输出“apply loop: 1”。。。就没有然后了=。=
所以,一定要避免dispatch_apply的嵌套调用。
灵活使用dispatch_group
很多时候我们需要等待一系列任务(block)执行完成,然后再做一些收尾的工作。如果是有序的任务,可以分步骤完成的,直接使用串行队列就行。但是如果是一系列并行执行的任务呢?这个时候,就需要dispatch_group帮忙了~总的来说,dispatch_group的使用分如下几步:
创建dispatch_group_t
添加任务(block)
添加结束任务(如清理操作、通知UI等)
下面着重讲讲在后面两步。
添加任务可以分为以下两种情况:
自己创建队列:使用dispatch_group_async。
无法直接使用队列变量(如使用AFNetworking添加异步任务):使用dispatch_group_enter,dispatch_group_leave。
自己创建队列时,当然就用dispatch_group_async函数,简单有效,简单例子如下:
dispatch_group_async(group, queue, ^{
当你无法直接使用队列变量时,就无法使用dispatch_group_async了,下面以使用AFNetworking时的情况:
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
dispatch_group_enter(group);
[manager GET:@"" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
dispatch_group_leave(group);
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
dispatch_group_leave(group);
使用dispatch_group_enter,dispatch_group_leave就可以方便的将一系列网络请求“打包”起来~
添加结束任务
添加结束任务也可以分为两种情况,如下:
在当前线程阻塞的同步等待:dispatch_group_wait。
添加一个异步执行的任务作为结束任务:dispatch_group_notify
这两个比较简单,就不再贴代码了=。=
使用dispatch_barrier_async,dispatch_barrier_sync的注意事项
dispatch_barrier_async的作用就是向某个队列插入一个block,当目前正在执行的block运行完成后,阻塞这个block后面添加的block,只运行这个block直到完成,然后再继续后续的任务,有点“唯我独尊”的感觉=。=
值得注意的是:
dispatchbarrier\(a)sync只在自己创建的并发队列上有效,在全局(Global)并发队列、串行队列上,效果跟dispatch_(a)sync效果一样。
既然在串行队列上跟dispatch_(a)sync效果一样,那就要小心别死锁!
dispatch_set_context与dispatch_set_finalizer_f的配合使用
dispatch_set_context可以为队列添加上下文数据,但是因为GCD是C语言接口形式的,所以其context参数类型是“void *”。也就是说,我们创建context时有如下几种选择:
用C语言的malloc创建context数据。
用C++的new创建类对象。
用Objective-C的对象,但是要用__bridge等关键字转为Core Foundation对象。
以上所有创建context的方法都有一个必须的要求,就是都要释放内存!,无论是用free、delete还是CF的CFRelease,我们都要确保在队列不用的时候,释放context的内存,否则就会造成内存泄露。
所以,使用dispatch_set_context的时候,最好结合dispatch_set_finalizer_f使用,为队列设置“析构函数”,在这个函数里面释放内存,大致如下:
void cleanStaff(void *context) {
dispatch_set_finalizer_f(queue, cleanStaff);
详细用法,请看我之前写的Blog
其实本文更像是总结了GCD中的“坑”=。=
至于经验,总结一条,就是使用任何技术,都要研究透彻,否则后患无穷啊~
感谢:http://tutuge.me//something-about-gcd/
浏览: 2572114 次
来自: China
很棒的一篇文章,感谢楼主分享
获取原型对象的三种方法&script&functi ...
属性的重写与删除与原型链无关&script&fun ...
为什么没生效啊!!!弄了几次了!!!
史上最详细的iOS之事件的传递和响应机制-原理篇http:// ...
(window.slotbydup=window.slotbydup || []).push({
id: '4773203',
container: s,
size: '200,200',
display: 'inlay-fix'GCD的延时操作 - 简书
GCD的延时操作
-(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent *)event{
//调用time延迟调用的方法
[self time];}-(void)time{
//after在...之后调度,在这里我们只需要填写时间(2.0 * NSEC_PER_SEC)及延迟2.0秒后需要进行的操作 NSLog
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@",[NSThread currentThread]);
NSLog(@"%@",[NSThread currentThread]);}
打印效果如下
15:41:27.789 GCD使用[]{number = 1, name = main}
15:41:29.984 GCD使用[]{number = 1, name = main}
逗 逗 逗 逗 逗 逗 逗 逗 逗 逗 逗 逗 逗 逗 逗 逗 逗 逗 逗 逗 逗 逗 逗 逗 逗ViewController.m
40-GCD 延迟执行(4)(掌握)
#import &ViewController.h&
@interface ViewController ()
@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 {
[self demo4];
- (void)download:(NSString *)url
NSLog(@&-----download:-----%@&,[NSThread currentThread]);
// 这种方法会卡住当前线程并等待时间结束后
- (void)demo1
NSLog(@&----touchesBegan111-----&);
[NSThread sleepForTimeInterval:3];
NSLog(@&----touchesBegan222-----&);
// 这种方法不会卡住当前线程,而是继续向下执行,等到时间结束的时候再返回到当前线程中执行(缺点:只能返回到当前线程接着进行,而没有办法开启新线程)
- (void)demo2
NSLog(@&----touchesBegan111-----&);
[self performSelector:@selector(download:) withObject:@&http://222222.jpg& afterDelay:3];
NSLog(@&----touchesBegan222-----&);
// 这是GCD提供的延时方法,这是将主队列传进去,延时过后block内的操作还是在主线程中执行,没有开启新的线程,也不会耽误当前操作(不会卡住线程)。和demo2很相似
- (void)demo3
NSLog(@&----touchesBegan111-----&);
NSLog(@&----%@----&,[NSThread currentThread]);
dispatch_queue_t queue = dispatch_get_main_queue(); // 可以先初始化好队列,然后将变量名传进去
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), queue, ^{
NSLog(@&主队列 延迟&);
NSLog(@&----%@----&,[NSThread currentThread]);
NSLog(@&----touchesBegan222-----&);
// 这也是GCD提供的延时方法,这是将 全局并行队列/串行队列 传进去,延时过后开启新的线程,block内的操作在新线程内执行
// 这里一定要注意,传 并行队列 或者 串行队列 都是会开启新线程的
注意:!!!!
GCD 延迟执行的本质!!!
延迟执行 == 延迟提交
   
A 先把任务提交到队列,然后3秒之后再执行该任务  错误
   
B 先等3秒的时间,然后再把任务提交到队列
- (void)demo4
NSLog(@&----touchesBegan111-----&);
NSLog(@&----%@----&,[NSThread currentThread]);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 可以在方法内部初始化队列,和demo3不同
NSLog(@&全局并发队列 延迟&);
NSLog(@&----%@----&,[NSThread currentThread]);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_queue_create(&name&, NULL), ^{
NSLog(@&串行队列队列 延迟&);
NSLog(@&----%@----&,[NSThread currentThread]);
NSLog(@&----touchesBegan222-----&);
本文已收录于以下专栏:
相关文章推荐
-(void)touchesBegan:(NSSetUITouch *& *)touches withEvent:(UIEvent
*)event{   
    [self
同步(sync) 和
异步(async) 的主要区别在于会不会阻塞当前线程,直到
Block 中的任务执行完毕!
如果是 同步(sync) 操作,它会阻塞当前线程并等待
Block 中的任...
上一篇文章我们介绍了GCD,以及其队列,同步异步等概念。这次我们将探讨更多GCD的功能,包括线程组(group),栅栏(barrier),单例(once),延时(after),并发迭代(apply)和...
★★ 同步和异步关注的是消息通信机制 (synchronous communication/ asynchronous communication)
★ 所谓同步,就是在发出一个调用时,在没有得到结...
1、Grand Central Dispatch是由苹果开发的一个多核编程的解决方案,自动管理现成的生命周期(创建线程、调度任务、销毁线程)。
2、主要包括任务(执行什么操作)和队列(用来存放...
之前写过一个简单的图片查看器,在图片单击,双击事件处理上遇到问题,通过延时单击事件操作。
详细:1.设置全局变量
bool doubleT在单击事件方法中设置延时操作
单击时进...
当执行那些与 UI 无关的任务,或者与 UI 交互的任务时,和执行其他任务一样,会需要大量时间,以上情况会经常出现。我们可以使用 dispatch_sync函数在一个分...
本章作为入门,我们将介绍介绍一下,能使用GCD执行UI操作。
2 代码实例
ZYViewController.h
@interface ZYViewCon...
Linux系统中定时任务的执行
Linux系统中延时任务的执行
Linux at命令用法示例
Linux crontab命令用法示例
系统临时文件管理
systemd-tmpfiles
iOS延时执行的四种方法#import &ViewController.h&@interface ViewController ()@end@implementation ViewController...
他的最新文章
讲师:王哲涵
讲师:韦玮
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)

我要回帖

更多关于 凯富k资产停止释放 的文章

 

随机推荐