剖析带有afterDealy参数的performSelector方法实现
2015-03-25 13:27
591 查看
主要从performSelector:afterDealy:的实现原理来分析为什么在主线程中调用此方法后不会阻塞主线程里业务代码的顺序执行。
示例代码如下:
- (void)doSomething {
NSLog(@"Begin doSomething...");
for (int i = 0; i < 10; i++) {
if (i == 5) {
NSLog(@"++++++++++%f", CFAbsoluteTimeGetCurrent());
[self performSelector:@selector(printIData:) withObject:@(i) afterDelay:0.2];
}
else {
[NSThread sleepForTimeInterval:0.2];
[self printIData:@(i)];
}
}
NSLog(@"Finish doSomething...");
}
- (void)printIData:(NSNumber *)i {
int iv = [i intValue];
if (iv == 5) {
NSLog(@"===================i is %d, %f", iv, CFAbsoluteTimeGetCurrent());
}
else {
NSLog(@"i is %d, %f", iv, CFAbsoluteTimeGetCurrent());
}
}
[[AboutRunloop sharedInstance] doSomething];
输出的结果是:
2014-03-19 14:44:26.054 RuntimePractice[9750:60b] i is 0, 416904266.054601
2014-03-19 14:44:26.256 RuntimePractice[9750:60b] i is 1, 416904266.256110
2014-03-19 14:44:26.457 RuntimePractice[9750:60b] i is 2, 416904266.457738
2014-03-19 14:44:26.659 RuntimePractice[9750:60b] i is 3, 416904266.659130
2014-03-19 14:44:26.860 RuntimePractice[9750:60b] i is 4, 416904266.860487
2014-03-19 14:44:26.861 RuntimePractice[9750:60b] ++++++++++416904266.861006
2014-03-19 14:44:27.061 RuntimePractice[9750:60b] i is 6, 416904267.061459
2014-03-19 14:44:27.263 RuntimePractice[9750:60b] i is 7, 416904267.263020
2014-03-19 14:44:27.464 RuntimePractice[9750:60b] i is 8, 416904267.464554
2014-03-19 14:44:27.666 RuntimePractice[9750:60b] i is 9, 416904267.666095
2014-03-19 14:44:27.666 RuntimePractice[9750:60b] Finish doSomething…
2014-03-19 14:44:27.672 RuntimePractice[9750:60b] =====i is 5, 416904267.672478
通过示例代码充分说明了:在主线程中调用performSelector:afterDealy方法后不会阻塞主线程里业务代码的顺序执行。
分析(主线程中调用):带有afterDealy:参数的performSelector方法内部采用了timer实现,我们知道timer不放到runloop里的话timer是不会触发的,所以这个timer肯定会放到runloop里,又因为主线程的runloop默认是运行着的,所以这个timer一定会被触发即Selector会被runloop触发回调。
回到示例程序上下文里面,当i==5时,执行了performSelector:afterDealy,所以此时一个timer源会被添加加到主线程的runloop里。又根据示例代码的日志输出可以看到doSomething 方法完全执行完后 runloop才触发i==5时的selector调用,这说明在线程中(包括主线程)是先执行线程的代码逻辑,最后才会检测Runloop里有没有注册的监听源,如果有则检测是不是应该触发源对应的外部处理方法,所以最后才会触发i==5的performSelector:afterDealy。
另注:
1) 示例代码中非i==5的情况下故意sleep了0.2秒(6/7/8/9 4个数x0.2 > delay的0.2),这是为了充分证明[self performSelector:@selector(printIData:) withObject:@(i) afterDelay:0.2];一定是在doSomething 方法执行完返回后再运行的。
2) [self performSelector:@selector(printIData:) withObject:@(i) afterDelay:0.2];中的0.2秒按timer的时间触发原理,它是Timer被加到runloop里后的0.2秒,但是在本示例代码环境下,线程里有长时任务正做着,所以这个触发的时间点是从timer被加到runloop里开始计时,等长时任务做完后的最近的0.2秒整数倍的时间点。
综上,performSelector带有afterDealy:参数的方法,哪怕是delay为0,也在当前线程的runloop里注册一个timer源,等线程里的逻辑做完后,会去检测runloop并按runloop里源注册的条件触发对应的事件处理方法,所以这个方法簇不会阻塞原线程中的代码执行流程。
参考:
1)方法的说明::NSObject Reference里performSelector:withObject:afterDelay:
2)Runloop相关:http://www.hrchen.com/2013/06/multi-threading-programming-of-ios-part-1/
3)Timer和Runloop相关: http://www.hrchen.com/2013/07/tricky-runloop-on-ios/
4)Timer的触发:http://www.cnblogs.com/smileEvday/archive/2012/12/21/NSTimer.html
示例代码如下:
- (void)doSomething {
NSLog(@"Begin doSomething...");
for (int i = 0; i < 10; i++) {
if (i == 5) {
NSLog(@"++++++++++%f", CFAbsoluteTimeGetCurrent());
[self performSelector:@selector(printIData:) withObject:@(i) afterDelay:0.2];
}
else {
[NSThread sleepForTimeInterval:0.2];
[self printIData:@(i)];
}
}
NSLog(@"Finish doSomething...");
}
- (void)printIData:(NSNumber *)i {
int iv = [i intValue];
if (iv == 5) {
NSLog(@"===================i is %d, %f", iv, CFAbsoluteTimeGetCurrent());
}
else {
NSLog(@"i is %d, %f", iv, CFAbsoluteTimeGetCurrent());
}
}
[[AboutRunloop sharedInstance] doSomething];
输出的结果是:
2014-03-19 14:44:26.054 RuntimePractice[9750:60b] i is 0, 416904266.054601
2014-03-19 14:44:26.256 RuntimePractice[9750:60b] i is 1, 416904266.256110
2014-03-19 14:44:26.457 RuntimePractice[9750:60b] i is 2, 416904266.457738
2014-03-19 14:44:26.659 RuntimePractice[9750:60b] i is 3, 416904266.659130
2014-03-19 14:44:26.860 RuntimePractice[9750:60b] i is 4, 416904266.860487
2014-03-19 14:44:26.861 RuntimePractice[9750:60b] ++++++++++416904266.861006
2014-03-19 14:44:27.061 RuntimePractice[9750:60b] i is 6, 416904267.061459
2014-03-19 14:44:27.263 RuntimePractice[9750:60b] i is 7, 416904267.263020
2014-03-19 14:44:27.464 RuntimePractice[9750:60b] i is 8, 416904267.464554
2014-03-19 14:44:27.666 RuntimePractice[9750:60b] i is 9, 416904267.666095
2014-03-19 14:44:27.666 RuntimePractice[9750:60b] Finish doSomething…
2014-03-19 14:44:27.672 RuntimePractice[9750:60b] =====i is 5, 416904267.672478
通过示例代码充分说明了:在主线程中调用performSelector:afterDealy方法后不会阻塞主线程里业务代码的顺序执行。
分析(主线程中调用):带有afterDealy:参数的performSelector方法内部采用了timer实现,我们知道timer不放到runloop里的话timer是不会触发的,所以这个timer肯定会放到runloop里,又因为主线程的runloop默认是运行着的,所以这个timer一定会被触发即Selector会被runloop触发回调。
回到示例程序上下文里面,当i==5时,执行了performSelector:afterDealy,所以此时一个timer源会被添加加到主线程的runloop里。又根据示例代码的日志输出可以看到doSomething 方法完全执行完后 runloop才触发i==5时的selector调用,这说明在线程中(包括主线程)是先执行线程的代码逻辑,最后才会检测Runloop里有没有注册的监听源,如果有则检测是不是应该触发源对应的外部处理方法,所以最后才会触发i==5的performSelector:afterDealy。
另注:
1) 示例代码中非i==5的情况下故意sleep了0.2秒(6/7/8/9 4个数x0.2 > delay的0.2),这是为了充分证明[self performSelector:@selector(printIData:) withObject:@(i) afterDelay:0.2];一定是在doSomething 方法执行完返回后再运行的。
2) [self performSelector:@selector(printIData:) withObject:@(i) afterDelay:0.2];中的0.2秒按timer的时间触发原理,它是Timer被加到runloop里后的0.2秒,但是在本示例代码环境下,线程里有长时任务正做着,所以这个触发的时间点是从timer被加到runloop里开始计时,等长时任务做完后的最近的0.2秒整数倍的时间点。
综上,performSelector带有afterDealy:参数的方法,哪怕是delay为0,也在当前线程的runloop里注册一个timer源,等线程里的逻辑做完后,会去检测runloop并按runloop里源注册的条件触发对应的事件处理方法,所以这个方法簇不会阻塞原线程中的代码执行流程。
参考:
1)方法的说明::NSObject Reference里performSelector:withObject:afterDelay:
2)Runloop相关:http://www.hrchen.com/2013/06/multi-threading-programming-of-ios-part-1/
3)Timer和Runloop相关: http://www.hrchen.com/2013/07/tricky-runloop-on-ios/
4)Timer的触发:http://www.cnblogs.com/smileEvday/archive/2012/12/21/NSTimer.html
相关文章推荐
- object_c 如何扩写NSObject 中的performSelector 方法实现传三个以上的参数
- 实现performSelector 传递多个参数
- 如何用perform selector调用超过两个以上参数的方法
- Java基础---基础加强---增强for循环、自动拆装箱及享元、枚举的作用、实现带有构造方法、透彻分析反射的基础_Class类、成员变量的反射、数组参数的成员方法进行反射、数组的反射应用
- ios如何用perform selector调用超过两个以上参数的方法
- 实现performSelector 传递多个参数
- 如何用perform selector调用超过两个以上参数的方法
- Java基础---基础加强---增强for循环、自动拆装箱及享元、枚举的作用、实现带有构造方法、透彻分析反射的基础_Class类、成员变量的反射、数组参数的成员方法进行反射、数组的反射应用
- 有关AE中Search(filter,Recycling)方法Recycling参数剖析
- JavaScript实现页面之间传递参数的方法
- Java常见笔试面试题目深度剖析系列之:Java方法参数传递详解
- php 动态执行带有参数的类方法
- php动态执行带有参数的类方法
- SQL 中传数组参数的变通方法:通过字符串分拆实现
- 超连接的方式给应用程序传递参数方法的实现
- PreparedStatement 或 CallableStatement,方法不能带有参数
- 实现URL参数的加密和解密的方法
- 文件大小带有逗号的实现方法
- 传递带有汉字参数的一种方法
- 深入剖析变长参数函数的实现