IOS开发中延迟执行和取消
在Objective-C中延迟执行还是很常见的需求,通常有如下几种方式可供选择:
performSelector:
想要延迟调用某个方法:
[selfperformSelector:@selector(delay)withObject:nilafterDelay:3.0];
取消延迟的方法:
[NSObjectcancelPreviousPerformRequestsWithTarget:selfselector:@selector(delay)object:nil];
这里需要注意参数需要保持一致,否则取消失败。
NSTimer
想要延迟调用某个方法:
self.timer=[NSTimerscheduledTimerWithTimeInterval:2.0target:selfselector:@selector(delay)userInfo:nilrepeats:NO];
取消延迟的方法:
[self.timerinvalidate]; GCD dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(2.0*NSEC_PER_SEC)),dispatch_get_main_queue(),^{ //... });
dispatch_after是比较常用的方法,但是Objective-C中并没有提供取消执行的相关API。我们只能自己实现这个取消的逻辑:
typedefvoid(^Task)(BOOLcancel); Taskdelay(NSTimeIntervaltime,void(^task)()){ __blockvoid(^closure)()=task; __blockTaskresult; TaskdelayedClosure=^(BOOLcancel){ if(closure){ void(^internalClosure)()=closure; if(!cancel){ dispatch_async(dispatch_get_main_queue(),internalClosure); } } closure=nil; result=nil; }; result=delayedClosure; dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(time*NSEC_PER_SEC)),dispatch_get_main_queue(),^{ if(delayedClosure){ delayedClosure(NO); } }); returnresult; } 使用的话可以这样: delay(60,^{ //... });
如果想要延迟,可以先声明成成员变量并赋值:
@property(copy,nonatomic)Tasktask; self.task=delay(60,^{ //... });
最后在需要的地方取消就行:
self.task(YES);
这种写法的核心思想是根据传入的Bool值,来控制dispatch_after回调block中的方法是否需要执行。看起来是取消了,但实际上还是被GCD放到RunLoop里去占用主线程资源了。
dispatch_source
我们还可以利用dispatch_source中的定时器,来实现延时/取消操作:
@property(strong,nonatomic)dispatch_source_ttimer; //队列 dispatch_queue_tqueue=dispatch_get_main_queue(); //创建dispatch_source dispatch_source_ttimer=dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,0,0,queue); //声明成员变量 self.timer=timer; //设置两秒后触发 dispatch_time_tstartTime=dispatch_time(DISPATCH_TIME_NOW,3.0*NSEC_PER_SEC); //设置下次触发事件为DISPATCH_TIME_FOREVER dispatch_time_tnextTime=DISPATCH_TIME_FOREVER; //设置精确度 dispatch_time_tleeway=0.1*NSEC_PER_SEC; //配置时间 dispatch_source_set_timer(timer,startTime,nextTime,leeway); //回调 dispatch_source_set_event_handler(timer,^{ //... }); //激活 dispatch_resume(timer);
需要取消的话:
dispatch_source_cancel(self.timer);