延时执行 - performSelector
performSelector在运行时去动态找方法,在编译时不做校验,所以编译期间不存在这个方法,是不会报错的。但是运行时不存在就会崩溃。为了程序的健壮性,在执行这个方法之前最好做一下判断:
// 判断是否有指定方法
+ (BOOL)respondsToSelector:(SEL)aSelector;
由于这个动态查找方法,可能存在方法不存在的问题,编译器会报警告⚠️, 可以采用下面的方法,让编译器忽略警告:
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[observer performSelector:info->_action withObject:change withObject:object];
#pragma clang diagnostic pop
1. 同步立刻执行
定义在<NSObject.h>文件中的三个方法,表示当前线程(不分主线程子线程)同步立刻执行方法。
最多只能传2个参数,如果有更多的参数,可以用字典、数组、NSInvocation 包装一下。
- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
2. 异步延迟执行
定义在<NSRunloop.h>文件中的 NSObject 的2个分类方法,表示当前线程(不分主线程子线程)异步延迟执行方法。
- 底层会创建一个 Timer,添加到当前线程的运行循环上去执行,可以指定加到什么mode上。
- 对于主线程来讲,运行循环自动开启,会执行成功。
- 对于子线程来讲,如果运行循环没开启,则不能执行成功。需要先调用完延迟执行方法后,再调用开启运行循环
[[NSRunLoop currentRunLoop]run];
因为源码逻辑中,开启某线程的Runloop,必须具有timer、source、observer任一事件才能触发开启。 - 注意:调用该方法所在的 VC 释放时主动调用取消函数,以确保不会内存泄露。
- (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray<NSRunLoopMode> *)modes;
- (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay;
// 取消执行
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget selector:(SEL)aSelector object:(nullable id)anArgument;
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget;
2.1 应用:防止暴力点击
1. 一段时间内多次点击只响应最后一次
狂点按钮,就会产生多次响应。如果想做到 2 秒内不论点击多少次,都只响应最后一次的话,可以使用延迟执行:
在点击方法内部先取消之前的延迟执行,再触发新一轮的延迟执行。这样在延迟时间 2 秒内的多次点击事件,都可以被取消掉,只响应最后一次。
- (IBAction)buttonClick:(id)sender {
id params;
[[self class]cancelPreviousPerformRequestsWithTarget:self
selector:@selector(realResponse:)
object:params];
[self performSelector:@selector(realResponse:)
withObject:params
afterDelay:2];
}
- (void)realResponse:(id)objcet {
NSLog(@"realResponse");
}
2.点击后,按钮置灰,一段时候后再恢复
-(void)buttonClicked:(id)sender{
self.button.enabled =NO;
[selfperformSelector:@selector(changeButtonStatus)withObject:nilafterDelay:1.0f];
}
-(void)changeButtonStatus{
self.button.enabled =YES;
}
3. 切换线程执行
waitUntilDone参数:
- YES 表示阻塞当前线程,等待这个方法执行完,再向下执行;
- NO 表示不等待方法执行完,直接向下执行。
- 线程执行完任务会立刻销毁
// 去主线程执行方法
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
// 去指定线程执行方法
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
// 开启一个子线程去后台执行方法
- (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));