前言

对RunLoop还没有什么概念的同学请移步我的上一篇文章,传送门:RunLoop入门 看我就够了
http://www.cnblogs.com/weiming4219/p/7879443.html
那么剩下的同学一定是对RunLoop有所了解的,那么我们就开始应用RunLoop吧~

应用

1.UIImageView的使用

a.目的

我们做这个应用的目的是为了了解RunLoop和performSelector这个方法的关系

b.过程

首先新建一个项目,在vc上拉一个UIImageView控件和UITextView控件。截图如下:

 

下面展示vc的.m代码

#import "ViewController.h"

@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *imageView; //刚刚拉的imageview
@end @implementation ViewController - (void)viewDidLoad {
[super viewDidLoad];
} - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self.imageView performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@""] afterDelay:2.0 inModes:@[NSDefaultRunLoopMode]];
}
代码很简单,无非就是想要让imageview在点击屏幕2秒钟后设置上图片。

注意:我们这里设置的mode为NSDefaultRunLoopMode,也就是默认状态。
跑起来!
发现没问题,点击一下屏幕,过了2秒后,imageview有了图片。图片:

恩,然后重跑一下,但是这次要拖拽textview了,发现有问题了:当点击屏幕后,马上拖textview数秒,发现图片2秒后没有显示出来。效果图:

现在回顾一下入门篇里面的Timer使用:
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerTest) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

当timer加到mode为kCFRunLoopDefaultMode时,拖拽textview时候timer就不起作用了;当mode为kCFRunLoopCommonModes时,拖拽textview不影响timer的使用,这里其实也是类似的。
所以机智的你应该已经知道怎么做了吧,没错,改一下mode就可以了~代码如下:

[self.imageView performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@""] afterDelay:2.0 inModes:@[NSRunLoopCommonModes]];
效果图就不上啦,偷个懒。

c.总结

这里的imageview其实只是一个例子,重要的是了解RunLoop和performSelector这个方法的关系!(敲黑板!)

2.线程与RunLoop的应用

a.目的

就如实验名,了解线程与RunLoop的关系及应用

b.过程

首先,在viewDidLoad中开启一个线程,并在线程对应的方法中打印该线程,这个很简单,我直接放截图了。

可以看到,线程打印出来了。
但是你知道这个线程什么时候销毁么。
先给个思路,想知道什么时候被销毁,就看什么时候调用dealloc方法就好了。
先创建一个继承NSThread的YQThread,并在.m文件里重写dealloc方法,代码如下:

- (void)dealloc
{
NSLog(@"YQThread被销毁");
}

然后修改vc中的线程,使用我们自定义的YQThread。再一次跑起来,看结果:

 

说明了开启线程后立马被销毁了。可是我如果不想让这个线程销毁呢,该怎么办。
我想到了强引用。上图:

 

看似可以了,线程好像没有被销毁。那么既然没有被销毁,我们就尝试再调用它喽。
在vc上加上如下代码:

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self performSelector:@selector(threadAgain) onThread:_thread withObject:nil waitUntilDone:NO];
} - (void)threadAgain
{
NSLog(@"--%@--", [NSThread currentThread]);
}

代码意思是当点击屏幕后,在_thread上调用threadAgain方法。结果如下:

 

你会发现当点击屏幕的时候没有输出。那就说明这个线程没用。看来还得我们的主角RunLoop出马啊。
在线程方法中,创建该线程对应的RunLoop。直接上代码:

- (void)threadTest
{
NSLog(@"--%@--", [NSThread currentThread]);
[[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
}

结果如下:

RunLoop应用7.gif

恩,有打印,说明线程还活着。但是你肯定会对刚刚那两行代码感到疑惑,什么意思呢?先看下面这个图:

 

就像上图画的,一个线程对应一个RunLoop,RunLoop里面有若干个mode,每个mode都有自己的内容,Source/Timer/Observer等等。上面的代码就相当于是给了当前的RunLoop添加了一个内容为Source的mode。而RunLoop有了mode才会有效果。
注意:mode里面必须要有内容!!!Source,Timer,Observer都可以。

c.总结

这就是RunLoop的一个功能,使线程保持生命。

d.拓展

刚刚那个应用本质上就是给线程加了一个RunLoop,然后RunLoop一直跑圈,所以线程也一直不会死。

1.拓展一

那么接下来我们来做个有趣的试验:在线程方法输出+++++++,其余都不变,代码如下:

- (void)viewDidLoad {
[super viewDidLoad]; _thread = [[YQThread alloc] initWithTarget:self selector:@selector(threadTest) object:nil];
[_thread start];
} - (void)threadTest
{
NSLog(@"--%@--", [NSThread currentThread]);
[[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run]; NSLog(@"++++++++");
} - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self performSelector:@selector(threadAgain) onThread:_thread withObject:nil waitUntilDone:NO];
} - (void)threadAgain
{
NSLog(@"--%@--", [NSThread currentThread]);
}

你觉得++++++++会打印出来么?结果如下:

恩,显然没有打印,为什么呢?先不解释原因,再看拓展二。

2.拓展二

再修改一下线程方法,这一次用一个while的死循环看看,其他不变。

- (void)threadTest
{
NSLog(@"--%@--", [NSThread currentThread]);
while () {
// 暂时不写任何东西
}
NSLog(@"++++++++");
}
++++++++会输出吗?有过编程经验的都知道不会,我也不截图展示了。

3.拓展三

上代码:

- (void)threadTest
{
NSLog(@"--%@--", [NSThread currentThread]);
[[NSRunLoop currentRunLoop] run];
NSLog(@"++++++++");
}

这里不给RunLoop设置内容,看看结果会怎么要。

 

发现点击屏幕没有输出线程,但是输出了+++++++。那是因为没有拓展二的while,线程方法会一行行执行下来。但是为什么点击屏幕没有输出呢,那是因为点击屏幕后,给RunLoop加了一个内容为sourse的mode(不懂的话看我的上一篇博客),但是!!!已经没有Run了!
什么意思呢,就是说当我们点击屏幕的时候,

 [[NSRunLoop currentRunLoop] run];

这一行代码已经在打开app的时候执行了,当我们点击屏幕的时候,已经没有Run了,而RunLoop实际上是没有跑起来的。
这就说明了,要让RunLoop跑起来,既要有有内容的mode,也需要 [[NSRunLoop currentRunLoop] run]; 。两个都不可少。

4.拓展四

这次还是用拓展二的代码,只是在while里面添上RunLoop代码,因为这个比较复杂,所以我放上完整代码:

- (void)viewDidLoad {
[super viewDidLoad]; _thread = [[YQThread alloc] initWithTarget:self selector:@selector(threadTest) object:nil];
[_thread start];
} - (void)threadTest
{
NSLog(@"--%@--", [NSThread currentThread]);
while () {
[[NSRunLoop currentRunLoop] run];
NSLog(@"++++++++");
}
} - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self performSelector:@selector(threadAgain) onThread:_thread withObject:nil waitUntilDone:NO];
} - (void)threadAgain
{
NSLog(@"--%@--", [NSThread currentThread]);
}

下面是结果:

 

仔细看输出,当点击屏幕之前,一直会输出+++++++,当点击后,+++++++输出停止,然后输出线程。下面来解释一下这个状况,先放图后解释:

这张图片应该不陌生,是苹果官方的。
当run起来后,因为while死循环,会一直输出+++++++,当点击屏幕的时候,会来到performSelector这个方法,而我们可以看到performSelector相当于是sourse,类似于port,所以当我们点击屏幕的时候,就相当于是给RunLoop添加了一个内容为sourse的mode,就相当于是

[[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];

而有了mode,RunLoop就开始有效起来了。然后再看while:

while (1) {
[[NSRunLoop currentRunLoop] run];
NSLog(@"++++++++");
}

因为RunLoop开始跑起来了,所以++++++又不会输出了,而这里相当于是上面的拓展一。

5.四个拓展的总结

先把拓展一的坑填了吧。看看拓展四再看看拓展一,都不打印++++++。其实本质上是一样的, NSLog(@"++++++++"); 这行代码之前已经RunLoop已经跑起来了,而至于为什么跑起来了就不会执行 NSLog(@"++++++++"); 了,可以看拓展二,相当于 NSLog(@"++++++++"); 有一个while循环!
再对上面四个拓展做一下总结:
a.要让RunLoop跑起来,必须要给其添加一个有内容的mode。而且必须要让他Run。
b.RunLoop跑起来后相当于是一个while的死循环,后面的代码不会执行。

注意!这四个拓展很有意思也很重要,如果觉得很乱,一定要多看几遍!!!

3.子线程中的Timer与RunLoop

最后一个实验:
直接放代码:

- (void)viewDidLoad {
[super viewDidLoad]; _thread = [[YQThread alloc] initWithTarget:self selector:@selector(threadTest) object:nil];
[_thread start];
} - (void)threadTest
{
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(TimerTest) userInfo:nil repeats:YES];
} - (void)TimerTest
{
NSLog(@"----%@-----", [NSThread currentThread]);
}

你觉得会有输出吗?

没有输出,为什么呢。想到上一篇有提到,就只有主线程的RunLoop会自动开启,而子线程的需要手动开启,而本次实验中,timer是子线程中的,而RunLoop没有开启,timer自然不会起到作用啦。
解决方法:开启RunLoop,怎么做?自己去试!!!

总结

其实本篇应用篇还是围绕着概念来的。还是那句话,光看是很难提升与理解的,还是要自己动手去实验,也许会有新的发现。最后,这篇文章是全是我试验出来的,概念也是自己琢磨出来,所以很有可能会有理解不当,如果有错误,一定要指出来!有问题可以在评论区留言~如果对你有帮助,点个赞呗~
好了,赶快滚去敲代码吧~

RunLoop已入门?赶紧来应用一下的更多相关文章

  1. RunLoop想入门,看这篇就够了

    前言 刚刚听到RunLoop的时候我也是一脸懵逼,这是什么,有什么用呢,逼格貌似还挺高.然后就开始尝试去搞懂它,去找博客,但是几乎所有的博客都是枯燥乏味的,都是讲概念,然后给个实例,对于我这个小白来说 ...

  2. NSTimer的使用[zhuang]

    NSTimer 的头文件 /* NSTimer.h Copyright (c) 1994-2015, Apple Inc. All rights reserved. */ #import <Fo ...

  3. RF《Quick Start Guide》操作总结

    这篇文章之所以会给整理出来,是因为学了一个季度的RF后,再去看官网的这个文档,感触破多,最大的感触还是觉得自己走了不少弯路,还有些是学习方法上的弯路.在未查看这类官网文档之前,更多的是看其他各种人的博 ...

  4. IOS 杂笔-5(NSTimer极浅析)

    1.timer都会对它的target进行retain,我们需要小心对待这个target的生命周期问题,尤其是重复性的timer. 2. timer不是一种实时的机制,会存在延迟,而且延迟的程度跟当前线 ...

  5. UIView之基础控件综合事例

    九宫格算法实现及优化 2015年11月19日 星期四 23:21 1.九宫格之初实现 要求: 1.1在主界面中,点击添加按钮就能添加商品,点击删除按钮就能删除商品; 1.2在初始化界面中,添加按钮处于 ...

  6. (转载)NSTimer

    转自:http://www.cnblogs.com/smileEvday/archive/2012/12/21/NSTimer.html 看到这个标题,你可能会想NSTimer不就是计时器吗,谁不会用 ...

  7. 关于NSRunLoop和NSTimer的深入理解

    一.什么是NSRunLoop NSRunLoop是消息机制的处理模式 NSRunLoop的作用在于有事情做的时候使的当前NSRunLoop的线程工作,没有事情做让当前NSRunLoop的线程休眠 NS ...

  8. 【实习记】2014-08-15文档太少看着源码用cgicc+stl库之模板谓词函数对象

        总结1: 今天找到了昨天scanf的问题答案,scanf与printf一样的神奇而复杂,稍不留神,就会被坑.scanf函数在读入非空白符分割的多个字符串的解决方法是这个:/* 以 | 分割 * ...

  9. masm32V11配置

    本文写给学汇编语言程序设计刚起步的吧友.适用Windows操作系统.已入门的吧友请绕道. (1)masm32开发包的下载 要用汇编语言编程,首先得有个开发工具,汇编语言开发工具有多种,但本文仅介绍ma ...

随机推荐

  1. 配置eNSP和本地电脑上的网卡相连,从而直接从本地电脑连接设备

  2. Echarts数据可视化visualMap,开发全解+完美注释

    全栈工程师开发手册 (作者:栾鹏) Echarts数据可视化开发代码注释全解 Echarts数据可视化开发参数配置全解 6大公共组件详解(点击进入): title详解. tooltip详解.toolb ...

  3. Weave Scope 多主机监控 - 每天5分钟玩转 Docker 容器技术(81)

    除了监控容器,Weave Scope 还可以监控 Docker Host. 点击顶部 HOSTS 菜单项,地图将显示当前 host. 与容器类似,点击该 host 图标将显示详细信息. host 当前 ...

  4. JAVAEE企业级应用开发浅谈第一辑

    不积跬步无以至千里,不积小流无以成江海 Step1.情景概要 作为一个JAVA WEB 开发人员,在开发web 项目时项目大家都有自己的一些新的体会,对于web 开发出现的一些比较经典的名词大家都会有 ...

  5. Delphi实现电脑端微信图片文件解密

    电脑端微信收到图片后是存在了“C:\Users\系统用户名\Documents\WeChat Files\微信帐号\Data”目录下的,但文件不能直接使用图片浏览器打开的,因为做了一些加密,之前有个朋 ...

  6. 关于master..xp_cmdshell’的几个错误_解决办法(转)

    错误一:Error Message:未能找到存储过程 'master..xp_cmdshell'. 第一步先删除:drop procedure sp_addextendedprocdrop proce ...

  7. win10 UWP button

    button有很多和wpf一样,可以看<深入浅出WPF> 我们可以在button的click写上 <Button Content="确定" Click=" ...

  8. Python学习笔记(四)

    Python学习笔记(四) 作业讲解 编码和解码 1. 作业讲解 重复代码瘦身 # 定义地图 nav = {'省略'} # 现在所处的层 current_layer = nav # 记录你去过的地方 ...

  9. PHP开发者必须了解的9个魔术方法

    这些'魔术'方法拥有者特殊的名字,以两个下划线开始,表示这些方法在PHP特定事件下将会被触发.这可能听起来有点自动魔法但是它真的很酷的,我们已经看过一个简单的例子在 last post,即我们使用一个 ...

  10. LINUX 笔记-文件属性相关命令

    chgrp:该命令用于改变文件所属用户组 chown:该命令用于改变文件的所有者 chmod:该命令用于改变文件的权限 -R:进行递归的持续更改,即连同子目录下的所有文件都会更改