本文屬筆記性質,主要針對本身理解不太透徹的地方進行記錄。ios
推薦系統直接學習小碼哥iOS底層原理班---MJ老師的課確實不錯,強推一波。數組
iOS系統在程序運行過程當中循環作一些事情bash
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
return 0; 則程序會直接退出。
}
}
複製代碼
[NSRunLoop currentRunLoop]; // 得到當前線程的RunLoop對象
[NSRunLoop mainRunLoop]; // 得到主線程的RunLoop對象
複製代碼
CFRunLoopModeRef表明RunLoop的運行模式網絡
一個RunLoop包含若干個Mode,每一個Mode又包含若干個Source0/Source1/Timer/Observerasync
RunLoop啓動時只能選擇其中一個Mode,做爲currentMode函數
若是須要切換Mode,只能退出當前Loop,再從新選擇一個Mode進入oop
不一樣組的Source0/Source1/Timer/Observer能分隔開來,互不影響性能
App的默認Mode,一般主線程是在這個Mode下運行學習
界面跟蹤 Mode,用於 ScrollView 追蹤觸摸滑動,保證界面滑動時不受其餘 Mode 影響ui
會進行
_commonModes
標記,只要_modes
符合_commonModes
都會執行。
_commonModeItems
表明着能在Common模式下工做的單元,好比timer。
Source0
去處理底層也是
NSTimer
runloop休眠以前刷新
清理內部須要釋放的對象
void observeRunLoopActicities(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info)
{
switch (activity) {
case kCFRunLoopEntry:
NSLog(@"kCFRunLoopEntry");
break;
case kCFRunLoopBeforeTimers:
NSLog(@"kCFRunLoopBeforeTimers");
break;
case kCFRunLoopBeforeSources:
NSLog(@"kCFRunLoopBeforeSources");
break;
case kCFRunLoopBeforeWaiting:
NSLog(@"kCFRunLoopBeforeWaiting");
break;
case kCFRunLoopAfterWaiting:
NSLog(@"kCFRunLoopAfterWaiting");
break;
case kCFRunLoopExit:
NSLog(@"kCFRunLoopExit");
break;
default:
break;
}
}
- (void)viewDidLoad {
// 建立Observer
CFRunLoopObserverRef observer = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, observeRunLoopActicities, NULL);
// 添加Observer到RunLoop中
CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);
// 釋放
CFRelease(observer);
}
複製代碼
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
// 處理一些子線程的邏輯
// 回到主線程去刷新UI界面
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"11111111111");
});
});
}
複製代碼
線程不會死,也能夠執行
test
中的代碼。可是run
方法裏的end
也不會打印,由於runloop已經休眠了。因爲任務永遠不會執行完畢,因此哪怕是VC釋放了,線程也不會釋放。
- (void)viewDidLoad {
[super viewDidLoad];
self.thread = [[MJThread alloc] initWithTarget:self selector:@sel(run) obj:nil];
[self.thread start];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:NO];
}
// 子線程須要執行的任務
- (void)test
{
NSLog(@"%s %@", __func__, [NSThread currentThread]);
}
- (void)run {
NSLog(@"%@----begin----", [NSThread currentThread]);
[[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
NSLog(@"%@----end----", [NSThread currentThread]);
}
複製代碼
NSRunLoop的run方法是沒法中止的,它專門用於開啓一個永不銷燬的線程(NSRunLoop)。
因此咱們哪怕咱們手動將runloop中止,也是作不到的。
咱們可使用另一個函數
runMode
來進行一次
runloop循環
但有一個問題是,只要該runloop被喚醒一次,就會退出
[[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
//[NSDate distantFuture] 會設置一個用不超時的時間
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
複製代碼
經過加標記的方式,讓runloop每次處理完時間,都從新runMode一次
self.thread = [[MJThread alloc] initWithBlock:^{
// 往RunLoop裏面添加Source\Timer\Observer
[[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
while (!weakSelf.isStoped) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
}];
複製代碼
- (void)viewDidLoad {
[super viewDidLoad];
static int count = 0;
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
NSLog(@"%d", ++count);
}];
// [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
// [[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
// NSDefaultRunLoopMode、UITrackingRunLoopMode纔是真正存在的模式
// NSRunLoopCommonModes並非一個真的模式,它只是一個標記
// timer能在_commonModes數組中存放的模式下工做
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}
複製代碼
performSelector:withObject:afterDelay:
須要依賴定時器與runloop,因此在自線程中並不會起做用。
- (void)test
{
NSLog(@"2");
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
NSLog(@"1");
// 這句代碼的本質是往Runloop中添加定時器
[self performSelector:@selector(test) withObject:nil afterDelay:.0];
NSLog(@"3");
});
}
//打印
// 1
// 3
複製代碼