RunLoop在實際開發過程中的應用(二)

Tip

  • 1.UIImageView延遲加載照片
  • 2.線程保活
  • 3.子線程中執行NSTimer
  • 4.performSelector
  • 5.自動釋放池
一.UIImageView延遲加載照片

在實際的開發過程中和面試題中,我們總去說可以在tableview的滑動的時候不去加載照片,因為渲染可能會阻塞主線程,我們總說:可以再tableview停止滑動的時候再去渲染照片,現在可以好好聊聊這個話題

//控制中寫上這個方法即可,3秒鐘然后顯示照片
//這個方法,我看就可以在滑動的時候,延遲顯示照片
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [self.imageview performSelector:@selector(setImage:)
                         withObject:[UIImage imageNamed:@"123.png"]
                         afterDelay:3];
}

//這個是第二種清空,然后直接指定什么模式,二者等價
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [self.imageview performSelector:@selector(setImage:)
                         withObject:[UIImage imageNamed:@"123.png"]
                         afterDelay:3
     inModes:@[NSDefaultRunLoopMode]];
}

滑動的時候,阻止了照片顯示,因為它默認是在Default下顯示, 此時tableView在滑動,RunLoop屬于Tracking的模式, 所以3秒鐘顯示的目的被延時了
  • 如果想讓照片的顯示或者timer的運行在任何時候都好使怎么辦?
    設置為commonMode就行了
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [self.imageview performSelector:@selector(setImage:)
                         withObject:[UIImage imageNamed:@"123.png"]
                         afterDelay:3
     inModes:@[NSRunLoopCommonModes]];
}
二.線程保活

可能你的項目中需要一個線程,一直在后臺做些耗時操作,但是不影響主線程,我們不要一直大量的創建和銷毀線程,因為這樣太浪費性能了,我們只要保留這個線程,只要對他進行“保活”就行

//繼承了一個NSTread 線程,然后使用vc中創建和執行某個任務,查看線程的情況
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    WXThread *thread = [[WXThread alloc] initWithTarget:self
                                               selector:@selector(doSomeThing)
                                                 object:nil];
    [thread start];
}
- (void)doSomeThing{
    NSLog(@"doSomeThing");
}
//每一次點擊屏幕的時候,線程執行完方法,直接釋放掉了,下一次創建了一個新的線程;
//子線程存活的時間很短,只要執行完畢任務,就會被釋放
2017-04-19 16:03:10.686 WXAllTest[14928:325108] doSomeThing
2017-04-19 16:03:10.688 WXAllTest[14928:325108] WXTread - dealloc - <WXThread: 0x600000276780>{number = 3, name = (null)}
2017-04-19 16:03:18.247 WXAllTest[14928:325194] doSomeThing
2017-04-19 16:03:18.249 WXAllTest[14928:325194] WXTread - dealloc - <WXThread: 0x608000271340>{number = 4, name = (null)}
2017-04-19 16:03:23.780 WXAllTest[14928:325236] doSomeThing
2017-04-19 16:03:23.781 WXAllTest[14928:325236] WXTread - dealloc - <WXThread: 0x608000270e00>{number = 5, name = (null)}

如果我每隔一段時間就像在線程中執行某個操作,好像現在不行
如果我們將線程對象強引用,也是不行的,會崩潰

1.成為基本屬性
/** 線程對象 */
@property(strong,nonatomic)  WXThread *thread;

2.創建線程之后,直接將入到RunLoop中
- (void)viewDidLoad {
    [super viewDidLoad];
    _thread = [[WXThread alloc] initWithTarget:self
                                      selector:@selector(doSomeThing)
                                        object:nil];
    [_thread start];
}

3.執行doSomeThing函數
- (void)doSomeThing{
    //一定要加入一個timer,port,或者是obervers,否則RunLoop啟動不起來
    [[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
    [[NSRunLoop currentRunLoop] run];
}

4.在點擊屏幕的時候,執行一個方法,線程之間的數據通信

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [self performSelector:@selector(test) onThread:_thread withObject:nil waitUntilDone:NO modes:@[NSDefaultRunLoopMode]];
}

5.將test方法寫清楚
- (void)test{
    NSLog(@"current thread - %@",[NSThread currentThread]);
}


//打印結果:同一個線程,線程保活成功
2017-04-19 18:21:07.660 WXAllTest[16145:382366] current thread - <WXThread: 0x60800007c180>{number = 3, name = (null)}
2017-04-19 18:21:07.843 WXAllTest[16145:382366] current thread - <WXThread: 0x60800007c180>{number = 3, name = (null)}
2017-04-19 18:21:08.015 WXAllTest[16145:382366] current thread - <WXThread: 0x60800007c180>{number = 3, name = (null)}
2017-04-19 18:21:08.194 WXAllTest[16145:382366] current thread - <WXThread: 0x60800007c180>{number = 3, name = (null)}
2017-04-19 18:21:08.398 WXAllTest[16145:382366] current thread - <WXThread: 0x60800007c180>{number = 3, name = (null)}
2017-04-19 18:21:08.598 WXAllTest[16145:382366] current thread - <WXThread: 0x60800007c180>{number = 3, name = (null)}
2017-04-19 18:21:08.770 WXAllTest[16145:382366] current thread - <WXThread: 0x60800007c180>{number = 3, name = (null)}
三.子線程中執行NSTimer

剛才學習了在RunLoop中去處理源,source (seletor),現在來看看如何在子線程中處理NSTimer

/** 線程對象 */
@property(strong,nonatomic)  WXThread *thread;
@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    
    //延遲圖片加載
    
    _thread = [[WXThread alloc] initWithTarget:self
                                      selector:@selector(execute)
                                        object:nil];
    [_thread start];
}

- (void)execute{
    //該方法默認不加入RunLoop中,使用schedule可以
    NSTimer *timer = [NSTimer timerWithTimeInterval:0.3
                                             target:self
                                           selector:@selector(test2)
                                           userInfo:nil
                                            repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
    [[NSRunLoop currentRunLoop] run];
}

- (void)test2{
    NSLog(@"current thread ********** 2 - %@",[NSThread currentThread]);
}

//打印:
2017-04-19 18:45:18.342 WXAllTest[16508:398383] current thread ********** 2 - <WXThread: 0x608000261380>{number = 3, name = (null)}
2017-04-19 18:45:18.643 WXAllTest[16508:398383] current thread ********** 2 - <WXThread: 0x608000261380>{number = 3, name = (null)}
2017-04-19 18:45:18.943 WXAllTest[16508:398383] current thread ********** 2 - <WXThread: 0x608000261380>{number = 3, name = (null)}
2017-04-19 18:45:19.243 WXAllTest[16508:398383] current thread ********** 2 - <WXThread: 0x608000261380>{number = 3, name = (null)}
2017-04-19 18:45:19.544 WXAllTest[16508:398383] current thread ********** 2 - <WXThread: 0x608000261380>{number = 3, name = (null)}
2017-04-19 18:45:19.842 WXAllTest[16508:398383] current thread ********** 2 - <WXThread: 0x608000261380>{number = 3, name = (null)}
2017-04-19 18:45:20.143 WXAllTest[16508:398383] current thread ********** 2 - <WXThread: 0x608000261380>{number = 3, name = (null)}
2017-04-19 18:45:20.443 WXAllTest[16508:398383] current thread ********** 2 - <WXThread: 0x608000261380>{number = 3, name = (null)}
2017-04-19 18:45:20.743 WXAllTest[16508:398383] current thread ********** 2 - <WXThread: 0x608000261380>{number = 3, name = (null)}
2017-04-19 18:45:21.042 WXAllTest[16508:398383] current thread ********** 2 - <WXThread: 0x608000261380>{number = 3, name = (null)}
2017-04-19 18:45:21.342 WXAllTest[16508:398383] current thread ********** 2 - <WXThread: 0x608000261380>{number = 3, name = (null)}

注意: 這兩者相同,后者默認直接創建了RunLoop,然后加進去了,但是一定要run才能啟動,但是過去我們沒有在主線程中run,也好使啊,為毛?因為在主線程中系統自動run了,否則線程早就停止了

    NSTimer *timer = [NSTimer timerWithTimeInterval:0.3
                                             target:self
                                           selector:@selector(test2)
                                           userInfo:nil
                                            repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
    [[NSRunLoop currentRunLoop] run];
    [NSTimer scheduledTimerWithTimeInterval:1
                                                      target:self
                                                    selector:@selector(test2)
                                                    userInfo:nil
                                                     repeats:YES];
    [[NSRunLoop currentRunLoop] run];

疑惑:滑動textview的時候,模式已經改成了Tracking,但是我們的timer是default模式,為什么沒有影響?

因為textView是主線程的,而timer是子線程的東西,所以沒關系

五.自動釋放池

1.什么是自動釋放池,就是將目前使用到的對象放到池子中,然后當自動釋放池銷毀的時候,對內部的所有對象都進行realese操作,retrainCount減一
2.自動釋放池什么時候銷毀和創建?
當每一次要進入睡眠狀態,那么就會銷毀,當即將醒來的時候,重新創建

一個RunLoop對應一個線程
建議每一次啟動RunLoop的時候,包裝一個自動釋放池,臨時創建了很多對象,等著我們釋放,在很多優秀的開源庫中,都有這個說明

- (void)viewDidLoad {
    [super viewDidLoad];
    
    //延遲圖片加載
    
    _thread = [[WXThread alloc] initWithTarget:self
                                      selector:@selector(execute)
                                        object:nil];
    [_thread start];
}

- (void)execute{
//    該方法默認不加入RunLoop中,使用schedule可以
    @autoreleasepool {
        NSTimer *timer = [NSTimer timerWithTimeInterval:0.3
                                                 target:self
                                               selector:@selector(test2)
                                               userInfo:nil
                                                repeats:YES];
        [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
        [[NSRunLoop currentRunLoop] run];
    }
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容