Runloop 多執行緒
1 負責程式不退出
2 負責監聽IOS所有的事件,如:觸控,時鐘,網路事件
3 如果沒有事件傳送,會讓程式進入休眠狀態
Runloop 的兩種模式:NSDefaultRunLoopMode+NSRunLoopCommonModesNSDefaultRunLoopMode:時鐘+網路事件
NSRunLoopCommonModes:使用者互動
來看看這段程式碼~思考下:以下程式碼的輸出是什麼?
int main(int argc, char * argv[]) {
@autoreleasepool {
NSLog(@"來了");
nil 就相當於UIApplication
int a = UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
NSLog(@"come here");
return a;
}
}
想好了嗎?揭曉答案了哦~答案是“來了”,你猜對了嗎?why?因為UIApplicationMain 有個死迴圈(runloop迴圈)
死迴圈的目的何在?
- 保證去程式不退出
- 負責監聽事件,觸控,時鐘,網路事件
- 如果沒有事件發生,會讓程式進入休眠狀態(也會消耗cpu的效能)
Runloop 的五種模式
1.UITrackingRunLoopMode UI模式
2.NSDefaultRunLoopMode 預設模式
3.NSRunLoopCommonModes 佔位模式相當於 UI模式+ 預設模式
4.UIInitializationRunLoopMode:在剛啟動App時進入的第一個Mode,啟動完成後不再使用
5.GSEventReceiveRunLoopMode:接受系統事件的內部Mode,通常用不到
有這樣一個場景 :介面上有個textview,當滾動textview,viewdidload 裡的timer就不工作了
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timeMethod) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop]addTimer:timer forMode:NSDefaultRunLoopMode];
我們來解釋下原因,請看下圖
此時的timer在預設模式下,蘋果的一貫注重使用者體驗,所以當滾動textview (使用者操作UI)的時候,runloop 會優先處理UI模式下的source。如果這時吧計時器加入到UI模式下,timeMethod 會正常進行,但當使用者停止操作UI,那麼UI模式下的runloop 就會進行休眠,所以timeMethod也會停止執行。此時我們可以吧timer加入到UI模式+預設模式。這時蘋果為了優化,產生了一個NSRunLoopCommonModes 佔位模式,但是它並不屬於Runloop的五大模式之一。
RunLoop && 多執行緒
- (void)viewDidLoad {
[super viewDidLoad];
NSThread *thread = [[NSThread alloc]initWithBlock:^{
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timeMethod) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop]addTimer:timer forMode:NSRunLoopCommonModes];
NSLog(@"來了1");
}];
[thread start];
}
- (void)timeMethod {
NSLog(@"來了2");
[NSThread sleepForTimeInterval:1];
NSLog(@"%@",[NSThread currentThread]);
}
2018-05-16 10:05:46.915347+0800 測試完[841:26856] 來了1
原因:執行緒走了,想要保證執行緒的命,就要讓執行緒有執行不完的任務,執行緒就不會釋放了~
- (void)viewDidLoad {
[super viewDidLoad];
NSThread *thread = [[NSThread alloc]initWithBlock:^{
NSTime*timer= [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timeMethod) userInfo:nilrepeats:YES];
[[NSRunLoop currentRunLoop]addTimer:timer forMode:NSRunLoopCommonModes];
//這個迴圈只能保證執行緒的命,但是timeMethod 還是不能呼叫
// while(true)
//{
//從事件佇列中取出事件來處理!!(但是平沒有開源這個,但是封裝了一個NSRunLoop 來處理這個事件)
// }
//RunLoop -- 一條執行緒上面的RunLoop模式是不迴圈的
//CFRunLoop -- currentRunLoop () 第一次獲取RunLoop的時候,建立RunLoop
[[NSRunLoop currentRunLoop]run];//死迴圈
NSLog(@"來了1");
}];
[thread start];
}
- (void)timeMethod {
NSLog(@"來了2");
[NSThread sleepForTimeInterval:1];
NSLog(@"%@",[NSThread currentThread]);
}
列印結果如下:
2018-05-11 10:06:44.594863+0800 RunloopTest[1300:42091] 來了2
2018-05-11 10:06:45.599998+0800 RunloopTest[1300:42091] <NSThread: 0x60400047df80>{number = 4, name = (null)}
主執行緒與子執行緒做的事情都是一樣的,唯一的區別是:UI介面在主線上面的;App啟動的第一條執行緒
蘋果為啥要吧UI介面放在主執行緒?為了安全+效率
三,CFRunloop 的一個用法
#import "ViewController.h"
typedef void(^RunloopBlock)(void);
@interface ViewController ()
@property(strong,nonatomic)NSMutableArray *tasks;
@end
@implementation ViewController
//這裡在cell 上載入圖片的耗時操作沒寫
/*分析卡頓的原因:
所有的Cell的載入都在主執行緒的一次Runloop迴圈中,UI渲染也屬於Runloop的事情,但是一次渲染18張圖片,渲染太多。導致卡頓
解決思路:一次Runloop迴圈,只載入一張圖片
步驟:
1.觀察(observer)Runloop的迴圈
2.一次Runloop迴圈,載入一張圖片
|-Cell載入圖片的方法放到陣列裡
|-Runloop迴圈 一次,就從陣列取出一個圖片載入
*/
- (void)viewDidLoad {
[self addRunloopObserver];
self.tasks = NSMutableArray.array;
[self addTask:^{
}];
}
- (void)addTask:(RunloopBlock)task{
//儲存任務到陣列
[self.tasks addObject:task];
if (self.tasks.count == 0) {
return;
}
if (self.tasks.count>18) {
[self.tasks removeObjectAtIndex:0];
}
}
- (void)addRunloopObserver{
//獲取當前的runloop
CFRunLoopRef runloop = CFRunLoopGetCurrent();
//定義上下文
/*
typedef struct {
CFIndex version;
void * info;
const void *(*retain)(const void *info);
void (*release)(const void *info);
CFStringRef (*copyDescription)(const void *info);
} CFRunLoopObserverContext;
*/
CFRunLoopObserverContext context = {
0,
(__bridge void *)self,
&CFRetain,
&CFRelease,
NULL
};
//建立觀察者
//c 中create new copy 堆區開闢記憶體空間。需要釋放
CFRunLoopObserverRef runLoopObserver = CFRunLoopObserverCreate(NULL, kCFRunLoopBeforeWaiting, YES, 0, &callback, &context);
CFRunLoopAddObserver(runloop, runLoopObserver, kCFRunLoopCommonModes);
//釋放
CFRelease(runloop);
}
void callback(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){
ViewController *vc = (__bridge ViewController *)info;
if (vc.tasks.count == 0) {
return;
}
RunloopBlock task = vc.tasks.firstObject;
task();
[vc.tasks removeObjectAtIndex:0];
};
相關文章
- iOS 多執行緒:『RunLoop』詳盡總結iOS執行緒OOP
- 多執行緒和多執行緒同步執行緒
- 多執行緒--執行緒管理執行緒
- 執行緒與多執行緒執行緒
- 多執行緒【執行緒池】執行緒
- runloop解決Cell上主執行緒卡頓OOP執行緒
- Java多執行緒-執行緒中止Java執行緒
- 多執行緒之初識執行緒執行緒
- iOS開發面試攻略(KVO、KVC、多執行緒、鎖、runloop、計時器)iOS面試執行緒OOP
- 多執行緒------執行緒與程式/執行緒排程/建立執行緒執行緒
- 多執行緒系列(1),多執行緒基礎執行緒
- a、多執行緒執行緒
- 多執行緒系列之 執行緒安全執行緒
- iOS 多執行緒之執行緒安全iOS執行緒
- Java多執行緒之執行緒中止Java執行緒
- Android多執行緒之執行緒池Android執行緒
- Java多執行緒-執行緒狀態Java執行緒
- Java多執行緒-執行緒通訊Java執行緒
- kuangshenshuo-多執行緒-執行緒池執行緒
- java 多執行緒守護執行緒Java執行緒
- Java多執行緒(2)執行緒鎖Java執行緒
- 多執行緒之手撕執行緒池執行緒
- java多執行緒9:執行緒池Java執行緒
- 【java多執行緒】(二)執行緒停止Java執行緒
- 執行緒以及多執行緒,多程式的選擇執行緒
- 多執行緒學習一(多執行緒基礎)執行緒
- Java多執行緒(一)多執行緒入門篇Java執行緒
- 多執行緒,多程式執行緒
- 【多執行緒總結(二)-執行緒安全與執行緒同步】執行緒
- 使用委託開啟多執行緒(多執行緒深入)執行緒
- 【Java多執行緒】輕鬆搞定Java多執行緒(二)Java執行緒
- 多執行緒(五)---執行緒的Yield方法執行緒
- 【Java多執行緒】執行緒安全的集合Java執行緒
- C#多執行緒(6):執行緒通知C#執行緒
- 【Java】【多執行緒】執行緒池簡述Java執行緒
- Java多執行緒-執行緒池的使用Java執行緒
- multiprocessing多執行緒未執行執行緒
- 多執行緒——GCD執行緒GC