RunLoop已入門?趕忙來應用一下

前言

對RunLoop尚未什麼概念的同窗請移步個人上一篇文章,傳送門:RunLoop入門 看我就夠了
http://www.cnblogs.com/weiming4219/p/7879443.html
那麼剩下的同窗必定是對RunLoop有所瞭解的,那麼咱們就開始應用RunLoop吧~html

應用

1.UIImageView的使用

a.目的

咱們作這個應用的目的是爲了瞭解RunLoop和performSelector這個方法的關係編程

b.過程

首先新建一個項目,在vc上拉一個UIImageView控件和UITextView控件。截圖以下:app


 

下面展現vc的.m代碼oop

#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:@"1"] afterDelay:2.0 inModes:@[NSDefaultRunLoopMode]];
}
代碼很簡單,無非就是想要讓imageview在點擊屏幕2秒鐘後設置上圖片。

注意:咱們這裏設置的mode爲NSDefaultRunLoopMode,也就是默認狀態。
跑起來!
發現沒問題,點擊一下屏幕,過了2秒後,imageview有了圖片。圖片:atom



恩,而後重跑一下,可是此次要拖拽textview了,發現有問題了:當點擊屏幕後,立刻拖textview數秒,發現圖片2秒後沒有顯示出來。效果圖:spa


如今回顧一下入門篇裏面的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:@"1"] afterDelay:2.0 inModes:@[NSRunLoopCommonModes]];
效果圖就不上啦,偷個懶。

c.總結

這裏的imageview其實只是一個例子,重要的是瞭解RunLoop和performSelector這個方法的關係!(敲黑板!)3d

2.線程與RunLoop的應用

a.目的

就如實驗名,瞭解線程與RunLoop的關係及應用code

b.過程

首先,在viewDidLoad中開啓一個線程,並在線程對應的方法中打印該線程,這個很簡單,我直接放截圖了。orm

能夠看到,線程打印出來了。
可是你知道這個線程何時銷燬麼。
先給個思路,想知道何時被銷燬,就看何時調用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 (1) {
        // 暫時不寫任何東西
    }
    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 (1) {
        [[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,怎麼作?本身去試!!!

總結

其實本篇應用篇仍是圍繞着概念來的。仍是那句話,光看是很難提高與理解的,仍是要本身動手去實驗,也許會有新的發現。最後,這篇文章是全是我試驗出來的,概念也是本身琢磨出來,因此頗有可能會有理解不當,若是有錯誤,必定要指出來!有問題能夠在評論區留言~若是對你有幫助,點個讚唄~好了,趕快滾去敲代碼吧~

相關文章
相關標籤/搜索