Re:從零開始的RunLoop實踐01

Re:從零開始的RunLoop實踐

本系列文章,因我在網上看了很多RunLoop的文章之后(先膜拜各路大牛),感覺自己大概懂了,但是說實戰一下,又無從下碼,本著寫不出來代碼,會再多理論好比有槍開不出子彈,所以盡量以解決開發中實際問題為出發點,主要以網絡上的博客和Github找到的代碼為基礎(這都是大牛的功勞),總結出用以實戰的幾個demo,主要為了以后自己使用查找方便(所以此系列提及的理論巨少,基本都是代碼,觀眾老爺也可以直接復制粘貼使用起來),公布在網絡上,歡迎各位指出錯誤,幫助本人及觀看文章的大家成長學習。

Re:從零開始的Runloop實踐01-線程常駐與銷毀

本片博文的代碼地址:

https://github.com/zyzhangyu/RunLoopDemo

本小節部分代碼取自http://weibo.com/1794363822/CvDEHwuvX?type=comment大牛的Github

強烈推薦同行關注此人

實踐目的:保證線程長存,有需求時工作,沒有時則休息,屬于性能優化的一個方法。

先用最簡單的方法實現:

   {    
      _zyThread = [[ZYThread alloc] initWithTarget:self selector:@selector(configRunLoop) object:nil];
        [_zyThread setName:@"章魚線程"];
        [_zyThread start];
    }```
    

    - (void)configRunLoop{
        
        @autoreleasepool {
            NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
            [runLoop addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
            [runLoop run];
        }
    }

使用`- (void)performSelector:(SEL)aSelector onThread`添加任務到RunLoop當中。示例如下:
```  [self performSelector:@selector(test) onThread:_zyThread withObject:nil waitUntilDone:NO];```
 

以上為最簡單的使用RunLoop實現線程常駐的方法,缺點是使用`[runLoop run];`開啟的RunLoop,在線程中使用`CFRunLoopStop(runLoopRef);`
也無法銷毀。

優點:簡單方便
缺點:難以結束
特點:實現了需要線程長存的目標,并且做到了有任務時執行,無任務時休眠的高效率。
可以改進的地方:
- 看不出哪里表現了,有任務時執行,無任務時休眠的效率
- 刪除困難,到需要結束時,難以結束

針對以上可以改進的兩點,才有了今天的主角—demo1.

核心代碼如下:

![Paste_Image.png](http://upload-images.jianshu.io/upload_images/163125-ca3ed38ea5ea8f9d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
使用do while循環來作為一個控制RunLoop是否繼續的一個開關。

核心代碼全貌(30行代碼,重寫在NSThread的子類下面):

![Paste_Image.png](http://upload-images.jianshu.io/upload_images/163125-d98000b48a13fc92.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
注釋1:
- 獲取當前的RunLoop,沒有的話會創建一個。
- 創建一個CFRunLoopObserverContext
- 創建一個CFRunLoopObserverRef
 這三個東西都是名詞,百度一搜,解釋的大把大把。

注釋2:為RunLoop添加觀察者

注釋3:如果當前線程有當前設置的runMode下的事件發生,runloop就會啟動,處理對應的事件。如果沒有事件發生,runloop就會在每一次   `NSDate distantFuture到來時,啟動一次當前線程的runloop.`

只要將控制循環是否繼續的Bool類型變量continued設置為No,線程就將會退出。
接下來是證明,Runloop會在有任務時執行,無任務時休眠的部分:

兩段代碼:

void currentRunLoopObserver(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info)
{
NSLog(@"Current thread Run Loop activity: %@", printActivity(activity));
}```

static inline NSString* printActivity(CFRunLoopActivity activity)
 {
     NSString *activityDescription;
     switch (activity) {
         case kCFRunLoopEntry:
             activityDescription = @"kCFRunLoopEntry";
             break;
         case kCFRunLoopBeforeTimers:
             activityDescription = @"kCFRunLoopBeforeTimers";
             break;
         case kCFRunLoopBeforeSources:
             activityDescription = @"kCFRunLoopBeforeSources";
             break;
         case kCFRunLoopBeforeWaiting:
             activityDescription = @"kCFRunLoopBeforeWaiting";
             break;
         case kCFRunLoopAfterWaiting:
             activityDescription = @"kCFRunLoopAfterWaiting";
             break;
         case kCFRunLoopExit:
             activityDescription = @"kCFRunLoopExit";
             break;
         default:
             break;
     }
     return activityDescription;
 }

解釋一下,上面代碼的作用就是—>在之前給RunLoop添加的觀察者,每個 Observer 都包含了一個回調(函數指針),當 RunLoop 的狀態發生變化時,觀察者就能通過回調接受到這個變化。回調內容是打印觀察到的狀態,可以觀測的時間點有以下幾個:

 typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
     kCFRunLoopEntry         = (1UL << 0), // 即將進入Loop
     kCFRunLoopBeforeTimers  = (1UL << 1), // 即將處理 Timer
     kCFRunLoopBeforeSources = (1UL << 2), // 即將處理 Source
     kCFRunLoopBeforeWaiting = (1UL << 5), // 即將進入休眠
     kCFRunLoopAfterWaiting  = (1UL << 6), // 剛從休眠中喚醒
     kCFRunLoopExit          = (1UL << 7), // 即將退出Loop
 };

這樣根據打印出來的狀態就可以確定,RunLoop到底是不是在有任務時執行,無任務時休眠了:

Paste_Image.png

第一個分隔線之前,為RunLoop添加了第一個任務—射擊,我們知道

線程和 RunLoop 之間是一一對應的,其關系是保存在一個全局的 Dictionary 里。線程剛創建時并沒有 RunLoop,如果你不主動獲取,那它一直都不會有。RunLoop 的創建是發生在第一次獲取時,RunLoop 的銷毀是發生在線程結束時。你只能在一個線程的內部獲取其 RunLoop(主線程除外)。
第一個分隔線之前,我們可以看到RunLoop中,執行第一個任務到任務結束后,進入到kCFRunLoopBeforeWaiting一個完整的狀態變化過程,這也能說明RunLoop在執行完任務之后,休息了。

第一個和第二個分隔線之間,顯示了,RunLoop從休息到喚醒在到休息的過程。

第二個分隔線之后,是RunLoop從喚醒到結束的一個過程。

這個Log可以和這張圖互相輔助來看:

Paste_Image.png

本片博文的代碼地址:

https://github.com/zyzhangyu/RunLoopDemo

參考資料:
http://weibo.com/1794363822/CvDEHwuvX?type=comment大牛的Github

http://blog.ibireme.com/2015/05/18/runloop/ 此篇為RunLoop寫的相當好的一篇 強烈建議大家學習 也希望我早日能寫出這種技術博文

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容