我理解的RunLoop

本人小白,歡迎各位大佬補(bǔ)充指點(diǎn)
先說說我是咋理解runloop的吧?然后在專業(yè)的講講

本人理解:

  1. 從C語言的角度來看,整個(gè)app就是調(diào)用了一個(gè)main函數(shù),只是這個(gè)main函數(shù)一直不返回值.
  2. main函數(shù)內(nèi)部:
    常識(shí):執(zhí)行任務(wù)需要開啟一個(gè)線程,因?yàn)槿蝿?wù)必須在線程下執(zhí)行
    一般的main函數(shù)執(zhí)行流程:開啟一個(gè)線程->添加許多任務(wù)(即寫了很多代碼)->任務(wù)完成(代碼執(zhí)行完畢)->線程關(guān)閉->返回main函數(shù)值程序結(jié)束
  3. runloop啥作用?
    Runloop就是一個(gè)死循環(huán),把這個(gè)死循環(huán)放在上面開啟的線程中,那么線程就永遠(yuǎn)不會(huì)結(jié)束.因此main永遠(yuǎn)不返回.但是這個(gè)死循環(huán)比較牛逼,下面講講他的牛逼之處:
    一般死循環(huán)特點(diǎn):
    1>程序一旦進(jìn)入死循環(huán),一直循環(huán)執(zhí)行,永不停歇,并且執(zhí)行速度非???br> 2>只執(zhí)行這段代碼,其他任何代碼都不會(huì)執(zhí)行,也就是說,只專注在這跑圈,不管外界發(fā)生任何事
    Runloop特點(diǎn):
    程序一旦進(jìn)入runloop,當(dāng)外界沒事的時(shí)候,他就慢慢跑圈,(至于多慢我就不知道了),甚至休眠,一旦有事,他就快速跑到該事件,去執(zhí)行該事件,執(zhí)行完畢后再接著休眠

下面專業(yè)系統(tǒng)的講一下:

RunLoop的傳說

  1. 什么是runloop?
    字面理解:運(yùn)行循環(huán)/跑圈
  2. 基本作用
    1>保持程序的持續(xù)運(yùn)行
    打開app它一直不死,就是因?yàn)閞unloop
    2>處理App中的各種事件(比如觸摸事件、定時(shí)器事件、Selector事件)
    3>節(jié)省CPU資源,提高程序性能:該做事時(shí)做事,該休息時(shí)休息
    能讓我們的主線程,有事情就做事情,沒事情就休息,比如點(diǎn)擊事件:點(diǎn)擊前主線程在休息,當(dāng)點(diǎn)擊事件處理完畢后,讓主線程再次休息…
  3. 如果沒有runloop
int main(int argc, char * argv[]) {
    NSLog(@"execute main function");
    return 0;
}

第3行后程序就結(jié)束了
如果有了runloop大致運(yùn)行代碼邏輯如下:

123.png

由于main函數(shù)里面( UIApplicationMain())啟動(dòng)了個(gè)RunLoop,所以程序并不會(huì)馬上退出,保持持續(xù)運(yùn)行狀態(tài)
main函數(shù)中的RunLoop

int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

代碼的UIApplicationMain函數(shù)內(nèi)部就啟動(dòng)了一個(gè)RunLoop
所以UIApplicationMain函數(shù)一直沒有返回,保持了程序的持續(xù)運(yùn)行
這個(gè)默認(rèn)啟動(dòng)的RunLoop是跟主線程相關(guān)聯(lián)的


既然runloop那么牛逼,那么我們就來認(rèn)識(shí)他一下:

RunLoop對象

iOS中有2套API來訪問和使用RunLoop:
Foundation(OC):NSRunLoop
Core Foundation(C):CFRunLoopRef
該框架有個(gè)特點(diǎn):所有的類都已CF開頭,以Ref(reference:引用)結(jié)尾
NSRunLoop和CFRunLoopRef都代表著RunLoop對象
NSRunLoop是基于CFRunLoopRef的一層OC包裝,所以要了解RunLoop內(nèi)部結(jié)構(gòu),需要多研究CFRunLoopRef層面的API(Core Foundation層面)

RunLoop與線程關(guān)系

上面說到,runloop通常是用來卡住線程的,那么他們必然有關(guān)系啦

  • 每條線程都有唯一的一個(gè)與之對應(yīng)的RunLoop對象
    主線程的RunLoop已經(jīng)自動(dòng)創(chuàng)建好了,并且主動(dòng)啟動(dòng)了,子線程的RunLoop需要主動(dòng)創(chuàng)建,然后手動(dòng)啟動(dòng)
  • RunLoop在第一次獲取時(shí)創(chuàng)建,在線程結(jié)束時(shí)銷毀

獲得RunLoop對象

  • Foundation
    [NSRunLoop currentRunLoop]; // 獲得當(dāng)前線程的RunLoop對象
    [NSRunLoop mainRunLoop]; // 獲得主線程的RunLoop對象
  • Core Foundation
    CFRunLoopGetCurrent(); // 獲得當(dāng)前線程的RunLoop對象
    CFRunLoopGetMain(); // 獲得主線程的RunLoop對象

上面講了3條理論,下面代碼證明一下:

- (void)viewDidLoad {
    [super viewDidLoad];
    //當(dāng)前線程就是主線程
    NSLog(@"%p--%p",[NSRunLoop mainRunLoop],[NSRunLoop currentRunLoop]);
    //創(chuàng)建一個(gè)線程
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
    [thread start]; 
}
-(void)run{
    /*
     [NSRunLoop currentRunLoop]:這就是在子線程中創(chuàng)建了一個(gè)runloop,不是通過alloc/init,而且該創(chuàng)建是懶加載的
     通過分析runloop(C語言)源碼過程如下:
     創(chuàng)建runloop函數(shù)只有一個(gè)參數(shù)t:線程(這也說明一個(gè)runloop只有一個(gè)線程)
     _CFRunLoopGet0(pthread_t t) {
     1.如果t==kNilPthreadT(0的意思),那么t就等于主線程
     __CFRunLoops:本身是一個(gè)可變字典,用于存儲(chǔ)所有的runloop,key值為線程,value為runloop
     __CFRunLoops :{
     mainthread : mainloop,
     thread1:loop1,
     ...
     }
     2.如果發(fā)現(xiàn)__CFRunLoops為空值
       2.1先創(chuàng)一個(gè)可變字典(就是__CFRunLoops)
       2.2根據(jù)主線程創(chuàng)建一個(gè)主runloop,即mainloop
       2.3將mainloop存入可變字典,key值為主線程
     3.根據(jù)傳進(jìn)來的線程到字典中拿到相應(yīng)的runloop
     4.沒有創(chuàng)建一個(gè)newloop->將newloop存入可變字典->返回這個(gè)newloop
     5.有就直接返回loop
     }
     */
    NSLog(@"%p",[NSRunLoop currentRunLoop]);
}

RunLoop的相關(guān)類

學(xué)到對象,那么當(dāng)然就有學(xué)到一些該對象相關(guān)的類了
Core Foundation中關(guān)于RunLoop的5個(gè)類
CFRunLoopRef
CFRunLoopModeRef
CFRunLoopSourceRef
CFRunLoopTimerRef
CFRunLoopObserverRef
這幾個(gè)類的關(guān)系圖如下:

123.png

分析:
RunLoop就像一個(gè)汽車,要跑起來,那么就要裝一些必備的東西

RunLoop里面有很多Mode對象->每個(gè)Mode對象里面又有三種集合分別存放:Source/Observer/Timer對象->有了這些Mode以及子內(nèi)容runloop才能一直跑圈/休息/有事件觸發(fā)
注意:runloop中的Mode中必須有source或者timer才能一直運(yùn)行,否則跑一圈就停了

下面分別講一講這幾個(gè)類:

CFRunLoopModeRef

  • CFRunLoopModeRef代表RunLoop的運(yùn)行模式
  • 一個(gè) RunLoop 包含若干個(gè) Mode,每個(gè)Mode又包含若干個(gè)Source/Timer/Observer
  • 每次RunLoop啟動(dòng)時(shí),只能指定其中一個(gè) Mode,這個(gè)Mode被稱作 CurrentMode
    [NSRunLoop currentRunLoop] runMode: beforeDate:
  • 如果需要切換Mode,只能退出Loop,再重新指定一個(gè)Mode進(jìn)入
    這樣做主要是為了分隔開不同組的Source/Timer/Observer,讓其互不影響
  • 系統(tǒng)默認(rèn)注冊了5個(gè)Mode:(前兩種最重要)
    kCFRunLoopDefaultMode: App的默認(rèn)Mode,通常主線程是在這個(gè)Mode下運(yùn)行
    UITrackingRunLoopMode:界面跟蹤 Mode,用于 ScrollView 追蹤觸摸滑動(dòng),保證界面滑動(dòng)時(shí)不受其他 Mode 影響
    分析:當(dāng)我們滾動(dòng)圖片時(shí)就會(huì)切換到這個(gè)模式,跟蹤你的滾動(dòng),一旦停止?jié)L動(dòng)就會(huì)切換到Default模式
    舉例:當(dāng)我們滾動(dòng)的時(shí)候,其他事件沒反應(yīng),比如定時(shí)器,當(dāng)滾動(dòng)的時(shí)候定時(shí)器沒反應(yīng),因?yàn)樗贒efault模式中
    UIInitializationRunLoopMode: 在剛啟動(dòng) App 時(shí)第進(jìn)入的第一個(gè) Mode,啟動(dòng)完成后就不再使用
    GSEventReceiveRunLoopMode: 接受系統(tǒng)事件的內(nèi)部 Mode,通常用不到
    kCFRunLoopCommonModes: 這是一個(gè)占位用的Mode,不是一種真正的Mode

CFRunLoopTimerRef

  • CFRunLoopTimerRef是基于時(shí)間的觸發(fā)器,基本上說的就是NSTimer
  • 它受runloop的Mode影響
  • GCD的定時(shí)器不受runLoop的Mode影響
    GCD定時(shí)器實(shí)現(xiàn)不一樣
    代碼舉例:
- (void)viewDidLoad {
    [super viewDidLoad];
    //向sb中拖入一個(gè)textView
//情況一:沒有滾動(dòng)textView時(shí),1秒打印一次,一旦滾動(dòng)textView,定時(shí)器無用了,停止?jié)L動(dòng),定時(shí)器有起作用了
//    [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(test) userInfo:nil repeats:YES];
    //上面一段代碼做了很多事情,等價(jià)代碼如下:
    //timer添加到defaultMode里面去,mode再添加到runloop中去,runloop在啟動(dòng)時(shí)候再指定這個(gè)mode->拿出timer來用
    NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(test) userInfo:nil repeats:YES];
    //也就說只能在Defaultmode下好使
//    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
    
    //情況二:如果想要一滾動(dòng)timer就有用,一停止?jié)L動(dòng)timer就失效,那么模式就用UITrackingRunLoopMode
//     [[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
    //情況三:滾動(dòng)不影響定時(shí)器
    //定時(shí)器只會(huì)跑在標(biāo)記為CommonModes的模式下(或者用GCD定時(shí)器)
      [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
    //通過打印runloop,我們發(fā)現(xiàn)kCFRunLoopDefaultMode/UITrackingRunLoopMode這兩種模式都被標(biāo)記為了commonmodes標(biāo)簽了,因此就可以做到了
    NSLog(@"%@",[NSRunLoop currentRunLoop]);  
}
- (void)test {
    NSLog(@"----");
}

CFRunLoopSourceRef

  • 系統(tǒng)定的我們用不著
  • CFRunLoopSourceRef是事件源(輸入源)
  • 我們的一些觸發(fā)事件,都是由source觸發(fā)的
    1>以前的分法
    Port-Based Sources
    Custom Input Sources
    Cocoa Perform Selector Sources(處理performselector調(diào)用)
    2>根據(jù)函數(shù)調(diào)用棧分法
    Source0:非基于Port
    Source1:基于Port的,通過內(nèi)核和其他線程通信,接收/分發(fā)系統(tǒng)事件

CFRunLoopObserverRef

  • CFRunLoopObserverRef是觀察者,能夠監(jiān)聽RunLoop的狀態(tài)改變
    可以監(jiān)聽的時(shí)間點(diǎn)有以下幾個(gè):
/* Run Loop Observer Activities */
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry = (1UL << 0),//即將進(jìn)入loop,左移動(dòng)0->2的0次放= 1
    kCFRunLoopBeforeTimers = (1UL << 1),//即將處理Timer …2
    kCFRunLoopBeforeSources = (1UL << 2),//即將處理source...4
    kCFRunLoopBeforeWaiting = (1UL << 5),//即將進(jìn)入休眠 …32
    kCFRunLoopAfterWaiting = (1UL << 6),//剛剛從休眠中喚醒...64
    kCFRunLoopExit = (1UL << 7),//即將推出loop 128
    kCFRunLoopAllActivities = 0x0FFFFFFFU
};

代碼舉例

  // 創(chuàng)建observer
    CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
        NSLog(@"----監(jiān)聽到RunLoop狀態(tài)發(fā)生改變---%zd", activity);
    });
    //添加觀察者到當(dāng)前線程的RunLoop的當(dāng)前Mode中,監(jiān)聽runloop的狀態(tài)
    CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
    // 釋放Observer
    CFRelease(observer);
    /*
     //需要自己管理內(nèi)存
     CF的內(nèi)存管理(Core Foundation)
     1.凡是帶有Create、Copy、Retain等字眼的函數(shù),創(chuàng)建出來的對象,都需要在最后做一次release
     * 比如CFRunLoopObserverCreate
     2.release函數(shù):CFRelease(對象);
     */

RunLoop的邏輯流程圖

456.png

RunLoop的應(yīng)用

  1. performSelector的使用
  //延遲3秒設(shè)置圖片并且值只在NSDefaultRunLoopMode模式下,也就是說,在滾動(dòng)的時(shí)候,不顯示圖片,滾動(dòng)停止,顯示圖片
    [self.imageView performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@"123"] afterDelay:3.0 inModes:@[NSDefaultRunLoopMode]
  1. 常駐線程
#import "ViewController.h"
#import "ZHThread.h"
@interface ViewController ()
@property (nonatomic,strong) ZHThread *thread;
@end

@implementation ViewController
/*
 常駐線程:讓一個(gè)線程永遠(yuǎn)不死
 通過繼承NSThread并且從寫delloc監(jiān)聽線程,可以看到,開啟線程后執(zhí)行完任務(wù)立馬死掉
 注意:不能簡單搞個(gè)屬性強(qiáng)引用,因?yàn)榫€程一旦執(zhí)行完畢,即使不釋放掉,當(dāng)前的線程也處于消亡狀態(tài),從新開啟([self.thread start])會(huì)崩潰
 當(dāng)然不通過start調(diào)用,通過performselector調(diào)用雖然不會(huì)崩潰,但是不會(huì)調(diào)用run2,因?yàn)榫€程處于消亡狀態(tài)
 //解決辦法:線程中添加runloop
 好處:可以隨時(shí)讓拿到這個(gè)線程讓他做一些事情.沒有做事情之前,這個(gè)runloop處于休眠狀態(tài),一旦調(diào)用該線程處理事件,就回喚醒runloop去執(zhí)行該線程的事件
 使用:搞一個(gè)常駐線程監(jiān)控聯(lián)網(wǎng)狀態(tài)
 原理:
 1.創(chuàng)建一個(gè)子線程并開啟線程
 2.在開啟方法(run)中添加一個(gè)runloop,并開啟runloop
 3.runloop會(huì)一直跑圈不會(huì)執(zhí)行完畢,阻止了開啟方法的執(zhí)行完畢即卡住子線程不讓他執(zhí)行完畢
 4.一旦子線程中有事件,runloop會(huì)被喚醒,讓改事件在該線程中執(zhí)行,一旦執(zhí)行完畢,繼續(xù)跑圈卡住該線程
 如果用一個(gè)死循環(huán)代替runloop是不可以的,因?yàn)樗姥h(huán)一直在處理這個(gè)事件,不會(huì)停下來先去執(zhí)行觸發(fā)事件
 */
- (void)viewDidLoad {
    [super viewDidLoad];
   self.thread = [[ZHThread alloc] initWithTarget:self selector:@selector(run) object:nil];
    //執(zhí)行線程
     [self.thread start];
}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [super touchesBegan:touches withEvent:event];
    //隨時(shí)拿到這個(gè)子線程執(zhí)行事件
    [self performSelector:@selector(run2) onThread:self.thread withObject:nil waitUntilDone:nil];
}
-(void)run{
    NSLog(@"=====run====");
    //run:默認(rèn)為NSDefaultRunLoopMode模式,時(shí)間是永遠(yuǎn),下面三行代碼等價(jià)
    //往里面添加source(mode中啥都沒有,runloop沒用)
    [[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
   //啟動(dòng)runloop
    [[NSRunLoop currentRunLoop] run];
//    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
//    [[NSRunLoop currentRunLoop] runUntilDate:[NSDate distantFuture]];
    NSLog(@"永遠(yuǎn)不會(huì)調(diào)用======");
}
-(void)run2{
     NSLog(@"=====run2====");
}

上面代碼用圖解如下:

111.png
  1. 在子線程搞個(gè)定時(shí)器一直調(diào)用
//創(chuàng)建子線程初始化調(diào)用這個(gè)方法(將上面viewdidload方法中run改為runx即可)
-(void)runx{
@autoreleasepool {
    //創(chuàng)建定時(shí)器
    NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run2) userInfo:nil repeats:YES];
    //將定時(shí)器添加到runloop
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
    [[NSRunLoop currentRunLoop] run];
    //或者
//    [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run2) userInfo:nil repeats:YES];
    //放在主線程中就不會(huì)用到這句,因?yàn)橹骶€程一直在run,不用手動(dòng)run
//    [[NSRunLoop currentRunLoop] run];
}
}
  1. 自動(dòng)釋放池
  • 將所有對象都放到這個(gè)池子中去,到池子釋放的時(shí)候給池子中的每一個(gè)對象release
  • 自動(dòng)釋放池什么時(shí)候釋放?(看源碼得到)
    runloop休眠之前會(huì)釋放一次,因?yàn)樾菝呖赡軙?huì)很長,如果不釋放,會(huì)堆積很多
    釋放后會(huì)同時(shí)創(chuàng)建一個(gè)新的釋放池,處理新一輪事件
    因此,在創(chuàng)建runloop的代碼外層最好包裝一層自動(dòng)釋放池
    比如上面的代碼

GCD的定時(shí)器使用

  • GCD的定時(shí)器不受runLoop的Mode影響(滾動(dòng)界面時(shí),定時(shí)器不受影響)
    GCD定時(shí)器實(shí)現(xiàn)不一樣
  • GCD比NSTimer準(zhǔn)確
    代碼舉例:
#import "ViewController.h"
@interface ViewController ()
/** 定時(shí)器(這里不用帶*,因?yàn)閐ispatch_source_t就是個(gè)類,內(nèi)部已經(jīng)包含了*) */
@property (nonatomic, strong) dispatch_source_t timer;
@end
@implementation ViewController
int count = 0;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    // 獲得隊(duì)列
    dispatch_queue_t queue = dispatch_get_main_queue();
    // 創(chuàng)建一個(gè)定時(shí)器(dispatch_source_t本質(zhì)還是個(gè)OC對象)
    self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    // 設(shè)置定時(shí)器的各種屬性(幾時(shí)開始任務(wù),每隔多長時(shí)間執(zhí)行一次)
    // GCD的時(shí)間參數(shù),一般是納秒(1秒 == 10的9次方納秒)NSEC_PER_SEC = 1s
    // dispatch_time(DISPATCH_TIME_NOW, 3.0 * NSEC_PER_SEC) 比當(dāng)前時(shí)間晚3秒
   //當(dāng)前時(shí)間直接寫:DISPATCH_TIME_NOW
    dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC));
    uint64_t interval = (uint64_t)(1.0 * NSEC_PER_SEC);
    dispatch_source_set_timer(self.timer, start, interval, 0);
    // 設(shè)置回調(diào)
    dispatch_source_set_event_handler(self.timer, ^{
        NSLog(@"------------%@", [NSThread currentThread]);
        count++;
                if (count == 4) {
                    // 取消定時(shí)器
                    dispatch_cancel(self.timer);
                    self.timer = nil;
                }
    });
    // 啟動(dòng)定時(shí)器
    dispatch_resume(self.timer);
}
@end

IOS程序啟動(dòng)與運(yùn)轉(zhuǎn)

注:這段是copy誰的我忘了,總結(jié)的很好,借用一下哈


從Xcode的線程函數(shù)調(diào)用棧(注意看看)可以看到一些方法調(diào)用順序。
(主線程開啟)start->(加載framework,動(dòng)態(tài)靜態(tài)鏈接庫,啟動(dòng)圖片,Info.plist,pch等)->main函數(shù)->UIApplicationMain函數(shù):{

  • 初始化UIApplication單例對象
  • 初始化AppDelegate對象,并設(shè)為UIApplication對象的代理
  • 檢查Info.plist設(shè)置的xib文件是否有效,如果有則解凍Nib文件并設(shè)置outlets,創(chuàng)建顯示key window、rootViewController、與rootViewController關(guān)聯(lián)的根view(沒有關(guān)聯(lián)則看rootViewController同名的xib),否則launch之后由程序員手動(dòng)加載。
  • 建立一個(gè)主事件循環(huán),其中包含UIApplication的Runloop來開始處理事件。
    }

UIApplication:

  1. 通過window管理視圖;
  2. 發(fā)送Runloop封裝好的control消息給target;
  3. 處理URL,應(yīng)用圖標(biāo)警告,聯(lián)網(wǎng)狀態(tài),狀態(tài)欄,遠(yuǎn)程事件等。

AppDelegate:

管理UIApplication生命周期和應(yīng)用的五種狀態(tài)(notRunning/inactive/active/background/suspend)。

Key Window:

  1. 顯示view;
  2. 管理rootViewcontroller生命周期;
  3. 發(fā)送UIApplication傳來的事件消息給view。
    rootViewController:
  4. 管理view(view生命周期;view的數(shù)據(jù)源/代理;view與superView之間事件響應(yīng)nextResponder的“備胎”);
  5. 界面跳轉(zhuǎn)與傳值;
  6. 狀態(tài)欄,屏幕旋轉(zhuǎn)。

view:

  1. 通過作為CALayer的代理,管理layer的渲染(順序大概是先更新約束,再layout再display)和動(dòng)畫(默認(rèn)layer的屬性可動(dòng)畫,view默認(rèn)禁止,在UIView的block分類方法里才打開動(dòng)畫)。layer是RGBA紋理,通過和mask位圖(含alpha屬性)關(guān)聯(lián)將合成后的layer紋理填充在像素點(diǎn)內(nèi),GPU每1/60秒將計(jì)算出的紋理display在像素點(diǎn)中。
  2. 布局子控件(屏幕旋轉(zhuǎn)或者子視圖布局變動(dòng)時(shí),view會(huì)重新布局)。
  3. 事件響應(yīng):event和guesture。

runloop:

  1. (要讓馬兒跑)通過do-while死循環(huán)讓程序持續(xù)運(yùn)行:接收用戶輸入,調(diào)度處理事件時(shí)間。
  2. (要讓馬兒少吃草)通過mach_msg()讓runloop沒事時(shí)進(jìn)入trap狀態(tài),節(jié)省CPU資源。

RunLoop面試題

  1. 什么是RunLoop?
    1>從字面意思看:運(yùn)行循環(huán)/跑圈
    2>其實(shí)它內(nèi)部就是do-while循環(huán),在這個(gè)循環(huán)內(nèi)部不斷地處理各種任務(wù)(比如:source/Timer/Observer)
    3>一個(gè)線程對應(yīng)一個(gè)runloop,主線程的runloop默認(rèn)已經(jīng)啟動(dòng),子線程的runloop需要自己手動(dòng)啟動(dòng)(調(diào)用run方法)
    4>RunLoop只能選擇一個(gè)Mode啟動(dòng),如果當(dāng)Mode中沒有任何source(source0/source1),Timer,那么就直接退出RunLoop
  2. 自動(dòng)釋放池什么時(shí)候釋放?
    通過Observer監(jiān)聽RunLoop的狀態(tài),一旦監(jiān)聽到RunLoop即將進(jìn)入休眠等待狀態(tài),就釋放自動(dòng)釋放池(KCFRunLoopBeforeWaiting)
  3. 在開發(fā)中如何使用RunLoop?什么應(yīng)用場景?
    1>開啟一個(gè)常駐線程(讓一個(gè)子線程不進(jìn)入消亡狀態(tài),等待其他的線程發(fā)來消息,處理其他事件)
    在子線程中開啟一個(gè)定時(shí)器
    在子線程中進(jìn)行一些長期監(jiān)控
    2>可以控制定時(shí)器在特定模式下運(yùn)行
    3>可以讓某些事件(行為/任務(wù))在特定模式下執(zhí)行
    4>可以添加Observer監(jiān)聽runloop的狀態(tài),比如監(jiān)聽點(diǎn)擊事件的處理(在所有點(diǎn)擊事件之前做一些事情)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容