文章目录

  • 1、performSelector实现原理
  • 2、performSelector触发时机
  • 3、performSelector 与 dispatch_source_t(事件联结)的区别
  • 4、注意点:Object传参
  • 5、应用:按钮防抖处理
  • 6、应用:延迟搜索设置
  • 7、拓展:RunLoop和线程
    • 7.1、RunLoop和线程的关系
    • 7.2、线程中RunLoop的生命周期

1、performSelector实现原理

performSelector依托于RunLoop执行,如果当前线程没有RunLoop的话,该方法就会失效。

用例证明:


- (void)viewDidLoad {[super viewDidLoad];NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(nslogHi) object:nil];[thread start];[self performSelector:@selector(nslogHello) onThread:thread withObject:nil waitUntilDone:NO];NSLog(@"_end");
}- (void)nslogHi {NSLog(@"hi....");
}- (void)nslogHello {NSLog(@"hello.....");
}/// 输出结果
2015-09-28 14:09:15.650 PCRunLoopThread[74414:5556013] hi....
2015-09-28 14:09:15.650 PCRunLoopThread[74414:5555961] _end

结论:

  • 1、线程在执行后会退出当前的RunLoop,也就是RunLoop会在一个线程结束时一同销毁。
  • 2、如果当前线程没有RunLoop的话,performSelector:onThread的方法也就失效。

延伸:如果想要把hello…打印出来,那么可以对这个线程设置成一直运行或者暂时阻塞一下,直至 nslogHello 方法运行结束。

2、performSelector触发时机

@property (nonatomic, strong) UIButton *testBtn;- (UIButton *)testBtn {if (!_testBtn) {_testBtn = [UIButton buttonWithType:UIButtonTypeCustom];_testBtn.frame = CGRectMake(100, 100, 100, 100);_testBtn.backgroundColor = [UIColor redColor];[_testBtn addTarget:self action:@selector(testAction:) forControlEvents:UIControlEventTouchUpInside];[self.view addSubview:_testBtn];}return _testBtn;
}- (void)testAction:(UIButton *)sender {NSLog(@"testAction >>>> before");[NSObject cancelPreviousPerformRequestsWithTarget:self];[self performSelector:@selector(delayAction:) withObject:@"delay" afterDelay:1.f];NSLog(@"testAction >>>> after");
}- (void)delayAction:(id)sender {NSLog(@"delayAction >>>> sender:%@",sender);
}- (void)viewDidLoad {[super viewDidLoad];[self testBtn];
}

1、点击一次:

2021-02-20 11:37:38.537893+0800 TestDemo[17102:8687749] testAction >>>> before
2021-02-20 11:37:38.538475+0800 TestDemo[17102:8687749] testAction >>>> after
2021-02-20 11:37:39.539686+0800 TestDemo[17102:8687749] delayAction >>>> sender:delay

2、连续多次点击

2021-02-20 11:48:12.665228+0800 TestDemo[17102:8687749] testAction >>>> before
2021-02-20 11:48:12.665806+0800 TestDemo[17102:8687749] testAction >>>> after
2021-02-20 11:48:12.836208+0800 TestDemo[17102:8687749] testAction >>>> before
2021-02-20 11:48:12.836669+0800 TestDemo[17102:8687749] testAction >>>> after
2021-02-20 11:48:12.993410+0800 TestDemo[17102:8687749] testAction >>>> before
2021-02-20 11:48:12.993900+0800 TestDemo[17102:8687749] testAction >>>> after
2021-02-20 11:48:13.153061+0800 TestDemo[17102:8687749] testAction >>>> before
2021-02-20 11:48:13.153571+0800 TestDemo[17102:8687749] testAction >>>> after
2021-02-20 11:48:14.154847+0800 TestDemo[17102:8687749] delayAction >>>> sender:delay

总结:

  • 1、performSelector: withObject: afterDelay: 这个方法是单线程的,也就是说只有当前调用此方法的函数执行完毕后,selector方法才会被调用。
  • 2、cancelPreviousPerformRequestsWithTarget 没有后续参数表示取消全部。

拓展
1、当第一次触发时,performSelector函数不作延迟;之后,每次触发延迟处理的情况时。可以设置延迟时间 delay 参数为可变参数。

3、performSelector 与 dispatch_source_t(事件联结)的区别

原理:
1、dispatch_source不依赖于Runloop,直接和底层内核交互,准确性更高。
2、performSelector依赖于Runloop运行,会因为主线被占用而产生延迟。
执行顺序:
1、dispatch_source在selector执行之前,需要触发事件,才产生事件联结。等selector中的方法执行完之后,才会出发下一次事件。
2、performSelector:只有当前调用此方法的函数执行完毕后,selector方法才会被调用。
应用场景:
1、dispatch_source:事件联结、高精度计时器
2、performSelector:时间延迟处理(按钮防抖处理)、即时搜索数据列表处理。

dispatch_source_merge_data的机制原理见:
Dispatch Source 应用

4、注意点:Object传参

@interface NSObject (NSDelayedPerforming)- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray *)modes;
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay;
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget selector:(SEL)aSelector object:(id)anArgument;
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget;@end

注意:以上方法中一个重要参数:anArgument,发现如果参数不为空,那取消时的参数也要一致,否则不能取消成功。

[self performSelector:@selector(didRuninCurrModel:) withObject:[NSNumber numberWithBool:YES] afterDelay:3.0f];
[self performSelector:@selector(didRuninCurrModelNoArgument) withObject:nil afterDelay:3.0f];

假如在三秒内执行以下取消方法,则结果分别为:

//可以取消成功。
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(didRuninCurrModel:) object:[NSNumber numberWithBool:YES]];
//不能取消成功。参数不匹配
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(didRuninCurrModel:) object:[NSNumber numberWithBool:NO]];
//不能取消成功。参数不匹配
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(didRuninCurrModel:) object:nil];//可以成功取消
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(didRuninCurrModelNoArgument) object:nil];//可以成功取消全部。
[NSObject cancelPreviousPerformRequestsWithTarget:self];
//可以成功取消全部。
[[self class] cancelPreviousPerformRequestsWithTarget:self];

5、应用:按钮防抖处理


#import <UIKit/UIKit.h>
#define kDefaultInterval 0.2  //默认时间间隔NS_ASSUME_NONNULL_BEGIN
@interface UIButton (AntiShake)/// 设置防抖时间间隔
@property (nonatomic, assign) NSTimeInterval antiShakeTimeInterval;@end
NS_ASSUME_NONNULL_END

#import "UIButton+AntiShake.h"
#import <objc/runtime.h>@interface UIButton()
/// 是否忽略点击事件
@property (nonatomic, assign) BOOL isIgnoreEvent;
@end@implementation UIButton (AntiShake)+ (void)load {static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{SEL selA = @selector(sendAction:to:forEvent:);SEL selB = @selector(mySendAction:to:forEvent:); // B是自己定义的方法Method methodA =   class_getInstanceMethod(self,selA);Method methodB = class_getInstanceMethod(self, selB);BOOL isAdd = class_addMethod(self, selA, method_getImplementation(methodB), method_getTypeEncoding(methodB));if (isAdd) {class_replaceMethod(self, selB, method_getImplementation(methodA), method_getTypeEncoding(methodA));}else{method_exchangeImplementations(methodA, methodB);}});
}- (void)mySendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event {if ([NSStringFromClass(self.class) isEqualToString:@"UIButton"]) {if (self.isIgnoreEvent) {return;}else {self.antiShakeTimeInterval = self.antiShakeTimeInterval <= 0 ?kDefaultInterval:self.antiShakeTimeInterval;}self.isIgnoreEvent = YES;[self performSelector:@selector(setIsIgnoreEvent:) withObject:[NSNumber numberWithBool:NO] afterDelay:self.antiShakeTimeInterval];}[self mySendAction:action to:target forEvent:event];
}#pragma mark - Setting & Getting
- (NSTimeInterval)antiShakeTimeInterval {return [objc_getAssociatedObject(self, _cmd) doubleValue];
}- (void)setAntiShakeTimeInterval:(NSTimeInterval)antiShakeTimeInterval {objc_setAssociatedObject(self, @selector(antiShakeTimeInterval), @(antiShakeTimeInterval), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}- (void)setIsIgnoreEvent:(BOOL)isIgnoreEvent {objc_setAssociatedObject(self, @selector(isIgnoreEvent), @(isIgnoreEvent), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}- (BOOL)isIgnoreEvent {return [objc_getAssociatedObject(self, _cmd) boolValue];
}@end

6、应用:延迟搜索设置

#pragma mark -
#pragma mark - UITextFieldDelegate
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {// 获取真实数据NSString * str = [textField.text stringByReplacingCharactersInRange:range withString:string];//延迟加载处理[self delaySearch:str];return YES;
}/// 延迟搜索设置
- (void)delaySearch:(NSString *)str {[NSObject cancelPreviousPerformRequestsWithTarget:self];[self performSelector:@selector(goSearch:) withObject:str afterDelay:1.f];
}/// 发起检索
static NSString *_citySearchHistory = @"";
- (void)goSearch:(NSString *)str {if (!kIsEmptyStr(str)) {// 避免同词多次请求if ([str isEqualToString:_citySearchHistory]) {return;} else {_citySearchHistory = str;}NSArray *arr = [AreaModel getSearchList:str];self.searchTableView.areaSearchArr = arr;} else {self.searchTableView.areaSearchArr = @[];}
}


7、拓展:RunLoop和线程

7.1、RunLoop和线程的关系

   每条线程都有唯一的一个与之对应的RunLoop对象,一个线程可以开启多个RunLoop,只不过都是嵌套在最大的RunLoop中,其关系是保存在一个全局的 Dictionary 里。

7.2、线程中RunLoop的生命周期

创建:
1、主线程:run loop默认是启动的,用于接收各种输入sources
2、子线程:线程刚创建时并没有 RunLoop,如果你不主动获取,那它一直都不会有。
在当前子线程中调用[NSRunLoop currentRunLoop]的时候,如果有就获取,没有就创建

启动:
1、主线程:默认是启动的
2、子线程:要手动添加

获取:
1、主线程:全局获取其RunLoop;[NSRunLoop mainRunLoop]或者 CFRunLoopGetMain();
2、子线程:只能在线程的内部获取其RunLoop;[NSRunLoop currentRunLoop]或者CFRunLoopGetCurrent();

销毁:
1、主线程:app结束时
2、子线程:子线程结束

链接拓展:
1、performSelector的常用方法
2、RunLoop 处理事件源-- performSelector方法

performSelector的原理及应用场景分析相关推荐

  1. 以太坊Bloom过滤器实现原理及应用场景分析

    一,Bloom过滤器的数据结构和reciept创建Bloom的过程 type Bloom [BloomByteLength]byte BloomByteLength = 256 Bloom 就是一个2 ...

  2. Serverless无服务器介绍、原理及应用场景分析

    一:什么是Serverless无服务器? serverless中文的含义是 "无服务器",但是它真正的含义是开发者再也不用过多考虑服务器的问题,但是并不代表完全去除服务器,而是我们 ...

  3. 以太坊源码深入分析(10)-- 以太坊Bloom过滤器实现原理及应用场景分析

    上一节分析reciept产生过程的时候提到:reciept会为日志数据生成一个Bloom过滤器,那Bloom过滤器是用来干嘛的呢?有什么用呢? 一,Bloom过滤器的数据结构和reciept创建Blo ...

  4. 语音验证码短信原理和应用场景分析

    引言 随着移动互联网的快速发展,短信验证码成为了许多应用和服务中常用的身份验证方式.然而,有时候用户可能由于各种原因无法接收到短信验证码,或者对于文字验证码不够方便或友好. 为了解决这些问题,语音验证 ...

  5. ConcurrentHashMap实现原理及源码分析

    ConcurrentHashMap是Java并发包中提供的一个线程安全且高效的HashMap实现(若对HashMap的实现原理还不甚了解,可参考我的另一篇文章HashMap实现原理及源码分析),Con ...

  6. 电商抢购秒杀系统的设计_1_应用场景分析

    2019独角兽企业重金招聘Python工程师标准>>> 电商抢购秒杀系统的设计_1_应用场景分析 概述 所谓知已知彼,百战不殆,在开始详细介绍实战中的抢购秒杀系统时,我们了解一些抢购 ...

  7. concurrenthashmap_ConcurrentHashMap实现原理及源码分析

    ConcurrentHashMap是Java并发包中提供的一个线程安全且高效的HashMap实现(若对HashMap的实现原理还不甚了解,可参考我的另一篇文章HashMap实现原理及源码分析),Con ...

  8. mysql 事务 查询 范围加锁_MySQL死锁系列-常见加锁场景分析

    本文我们就从原理走向实战,分析常见 SQL 语句的加锁场景.了解了这几种场景,相信小伙伴们也能举一反三,灵活地分析真实开发过程中遇到的加锁问题. 如下图所示,数据库的隔离等级,SQL 语句和当前数据库 ...

  9. SIFT原理与源码分析:DoG尺度空间构造

    <SIFT原理与源码分析>系列文章索引:http://blog.csdn.net/xiaowei_cqu/article/details/8069548 尺度空间理论 自然界中的物体随着观 ...

最新文章

  1. 机械转电子工程嵌入式方向靠谱吗?怎么上手学习?
  2. linux :YOLO5配置(ubuntu 20.04 下安装运行yolov5)
  3. 选择 SAP Spartacus 作为 SAP Commerce Cloud Storefront 实现框架的五个理由
  4. php中日期选择代码,实现JS日期时间选择器
  5. bean json转kotlin_Android--------kotlin插件神器Json直接生成javaBean
  6. 【UOJ139】【UER #4】被删除的黑白树
  7. oracle 计算复杂 数据跑不出来 如何分批次_如何配置PG的数据库缓冲
  8. Leetcode每日一题:58.length-of-last-word(最后一个单词的长度)
  9. linux服务端口加密,linux – 如何通过单个端口处理加密和未加密的http连接
  10. hermite插值c语言程序,张艳-埃尔米特Hermite 插值逼近的C语言程序.doc
  11. 如何设计一个好看的 404 错误页面 ?
  12. 《2019上半年网络工程师考试大纲》
  13. JAVA中什么 和 什么的区别--面试最经常问的(全)
  14. Java项目使用jib打包docker镜像的简单记录
  15. 家政服务app软件开发
  16. 计算机 蓝牙鼠标卡顿,无线蓝牙鼠标为什么有时会卡顿发飘,不稳定?
  17. 0.91寸 SSD1306 OLED介绍(四) --- 用上位机验证OLED显示屏
  18. 计算机网络的简单实验
  19. Firefly ROC-RK3588S-PC板卡详细介绍
  20. 从满腹经纶到入行小白:理论学习与实际应用的差距

热门文章

  1. 集成学习精讲01 - SAP大神黄佳新作《零基础学机器学习》节选
  2. 分布式流式计算框架Storm
  3. K2数据库database
  4. C语言给出点坐标进行克里金插值,Arcgis笔记之克里金插值——求助surfer8.0
  5. 微商成功神器,python程序员教你,一键分析微信好友近期所有信息
  6. 自动计数报警器c语言程序,简易STC15F104E单片机定时报警器制作 附程序
  7. QFP PQFP LQFP TQFP封装形式及PCB详解!
  8. 实战 | 文件下载、及浏览器加速导致不能下载的问题
  9. EasyNVR+人工智能分析算法,赋能AI行业应用
  10. 简单的掷骰子游戏(Java、UI界面)