RunLoop

Runloop

運行循環(huán),在程序運行過程中循環(huán)做一些事情.

應(yīng)用范疇:

  • 保持程序的持續(xù)運行
  • 定時器 performSelector
  • GCD Async Main Queue
  • 事件響應(yīng) 手勢識別 界面刷新
  • 網(wǎng)絡(luò)請求
  • AutoreleasePool

Runloop與線程

  • 每條線程都有唯一的一個與之對應(yīng)的Runloop對象.
  • Runloop保存在一個全局的字典里面,線程最為.key,runloop最為value.
  • 第一次獲取的時候自動創(chuàng)建.
  • Runloop線程結(jié)束時候銷毀.
  • 主線程的Runloop自動創(chuàng)建,子線程默認沒有開啟Runloop.

獲取RunLoop對象

  • Foundation:NSRunloop
  • Core Foundation:CFRunloopRef
    NSRunLoop *runloop;
    CFRunLoopRef runloop2;
    
    runloop = [NSRunLoop currentRunLoop];
    runloop2 = CFRunLoopGetCurrent();
    [NSRunLoop mainRunLoop];//主線程runloop

RunLoop相關(guān)的類

  • CFRunLoopRef
  • CFRunLoopModeRef
  • CFRunLoopSourceRef
  • CFRunLoopTimeRef
  • CFRunLoopObserverRef

CFRunLoopRef 底層結(jié)構(gòu)

typedef struct CF_BRIDGED_MUTABLE_TYPE(id) __CFRunLoop * CFRunLoopRef;
struct __CFRunLoop {
    ...
    pthread_t _pthread;
    uint32_t _winthread;
    CFMutableSetRef _commonModes;
    CFMutableSetRef _commonModeItems;
    CFRunLoopModeRef _currentMode;
    CFMutableSetRef _modes;//集合包含各種CFRunLoopModeRef對象
    ...
};

CFRunLoopModeRef底層結(jié)構(gòu)

struct __CFRunLoopMode {
    ...
    CFStringRef _name;
    CFMutableSetRef _sources0;//(集合包含CFRunLoopSourceRef)
    CFMutableSetRef _sources1;
    CFMutableArrayRef _observers;//數(shù)組包含CFRunLoopObserverRef
    CFMutableArrayRef _timers;//數(shù)組包含CFRunLoopTimeRef
    ...
};

CFRunLoopModeRef

常見2種mode

  • kCFRunLoopDefaultModel(NSDefaultRunLoopMode),App默認mode,通常主線程處于這個mode下.
  • UITrackingRunLoopMode:界面跟蹤Mode,用于ScrollView追蹤滑動觸摸,保證界面滑動時候不受其他mode影響.

source0

  • 觸摸事件處理
  • performSelectoreThread

source1

  • 基于port的線程間通信
  • 系統(tǒng)事件捕捉

timers

  • NSTimer
  • performSelector: withObject: afterDelay:

observers

  • 監(jiān)聽runloop狀態(tài)
  • UI刷新

CFRunLoopObserverRef

有幾種模式

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    NSRunLoop *runloop;
    CFRunLoopRef runloop2;
    
    runloop = [NSRunLoop currentRunLoop];
    runloop2 = CFRunLoopGetCurrent();
    
    
    CFRunLoopObserverRef observer = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, MyCFRunLoopObserverCallBack, NULL);
    CFRunLoopAddObserver(runloop2, observer, kCFRunLoopCommonModes);
    CFRelease(observer);
    
}

/*
 
 kCFRunLoopEntry = (1UL << 0),
 kCFRunLoopBeforeTimers = (1UL << 1),
 kCFRunLoopBeforeSources = (1UL << 2),
 kCFRunLoopBeforeWaiting = (1UL << 5),
 kCFRunLoopAfterWaiting = (1UL << 6),
 kCFRunLoopExit = (1UL << 7),
 kCFRunLoopAllActivities = 0x0FFFFFFFU
 
 */
void MyCFRunLoopObserverCallBack(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;
    }
    
}

  • CFRunLoopModeRef代表RunLoop的運行模式.
  • 一個RunLoop包含若干個Mode,每個Mode又包含若干個/sources0/sources1/observers/timers.
  • RunLoop啟動時候只能選擇一個mode,作為currentMode.
  • 切換Mode,只能退出當(dāng)前Loop,再重新選擇mode.

流程圖

實際應(yīng)用

  • 控制線程聲明周期(線程保活)
  • 解決NSTimer在滑動時候停止工作
  • 監(jiān)控應(yīng)用卡頓
  • 性能優(yōu)化

線程保活

@interface ViewController ()

@property (nonatomic,strong) MyThread *thread;
@property (nonatomic,assign) BOOL isStop;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    [self.thread start];

}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    if (self.isStop) {
        return;
    }
    [self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:YES];
}

- (void)test{
    NSLog(@"test");
    
}

- (IBAction)stopXib:(id)sender {
    if (self.isStop) {
        return;
    }
    [self performSelector:@selector(stopThread) onThread:self.thread withObject:nil waitUntilDone:YES];
}

- (void)stopThread{
    self.isStop = YES;
    CFRunLoopStop(CFRunLoopGetCurrent());
}

- (MyThread *)thread{
    if (!_thread) {
        __weak typeof(self) weakSelf = self;
        _thread = [[MyThread alloc]initWithBlock:^{
            NSLog(@"---begin----%@",[NSThread currentThread]);
            
            [[NSRunLoop currentRunLoop]addPort:[NSPort new] forMode:NSDefaultRunLoopMode];
//            [[NSRunLoop currentRunLoop]run];
            while (weakSelf && !weakSelf.isStop) {
                [[NSRunLoop currentRunLoop]runMode:NSDefaultRunLoopMode beforeDate: [NSDate distantFuture]];
            }
            
            NSLog(@"---end----%@",[NSThread currentThread]);
            
        }];
    }
    return _thread;
}

- (void)viewWillDisappear:(BOOL)animated{
    [super viewWillDisappear:animated];
    
}

- (void)dealloc{
    NSLog(@"%s",__func__);
    [self stopXib:nil];
    
}

接口封裝
.h

typedef void(^MyTaskBlock)(void);
@interface MyPermenantThread : NSObject

- (void)run;

- (void)executeTask:(MyTaskBlock)block;

- (void)stop;

@end

.m

@interface MyPermenantThread()

@property (nonatomic,strong) MyThread *innerThread;
@property (nonatomic,assign,getter=isStopped) BOOL stopped;
@end

@implementation MyPermenantThread

- (instancetype)init
{
    self = [super init];
    if (self) {
        __weak typeof (self) weakSelf = self;
        self.innerThread = [[MyThread alloc]initWithBlock:^{
           
            [[NSRunLoop currentRunLoop]addPort:[NSPort new] forMode:NSDefaultRunLoopMode];
            
            while (weakSelf && !weakSelf.isStopped) {
                [[NSRunLoop currentRunLoop]runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
            };
            
        }];
        
    }
    return self;
}


- (void)run{
    if (!self.innerThread) {
        return;
    }
    [self.innerThread start];
}

- (void)executeTask:(MyTaskBlock)block{
    if (!self.innerThread || !block) {
        return;
    }
    [self performSelector:@selector(__executeTask:) onThread:self.innerThread withObject:block waitUntilDone:NO];
}

- (void)stop{
    if (!self.innerThread) {
        return;
    }
    [self performSelector:@selector(__stop) onThread:self.innerThread withObject:nil waitUntilDone:YES];
}

- (void)dealloc{
    [self stop];
}

#pragma mark -- private methods
- (void)__stop{
    self.stopped = YES;
    CFRunLoopStop(CFRunLoopGetCurrent());
    self.innerThread = nil;
}

- (void)__executeTask:(MyTaskBlock)block
{
    block();
}

@end

c語言實現(xiàn)

            NSLog(@"begin");
            
            CFRunLoopSourceContext context = {0} ;
            
            CFRunLoopSourceRef source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
            
            CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
            
             while (weakSelf && !weakSelf.isStopped) {
                 //第三個參數(shù) 執(zhí)行完退出YES.
                 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e10, true);
            }
            
            CFRelease(source);
            
            NSLog(@"end");
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末肩钠,一起剝皮案震驚了整個濱河市冠绢,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌诈皿,老刑警劉巖逸绎,帶你破解...
    沈念sama閱讀 223,002評論 6 519
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件时肿,死亡現(xiàn)場離奇詭異赃蛛,居然都是意外死亡恃锉,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,357評論 3 400
  • 文/潘曉璐 我一進店門呕臂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來破托,“玉大人,你說我怎么就攤上這事歧蒋⊥辽埃” “怎么了?”我有些...
    開封第一講書人閱讀 169,787評論 0 365
  • 文/不壞的土叔 我叫張陵谜洽,是天一觀的道長萝映。 經(jīng)常有香客問我,道長阐虚,這世上最難降的妖魔是什么序臂? 我笑而不...
    開封第一講書人閱讀 60,237評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮实束,結(jié)果婚禮上奥秆,老公的妹妹穿的比我還像新娘。我一直安慰自己咸灿,他們只是感情好构订,可當(dāng)我...
    茶點故事閱讀 69,237評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著避矢,像睡著了一般悼瘾。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上审胸,一...
    開封第一講書人閱讀 52,821評論 1 314
  • 那天分尸,我揣著相機與錄音,去河邊找鬼歹嘹。 笑死箩绍,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的尺上。 我是一名探鬼主播材蛛,決...
    沈念sama閱讀 41,236評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼怎抛!你這毒婦竟也來了卑吭?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,196評論 0 277
  • 序言:老撾萬榮一對情侶失蹤马绝,失蹤者是張志新(化名)和其女友劉穎豆赏,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,716評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡掷邦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,794評論 3 343
  • 正文 我和宋清朗相戀三年白胀,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片抚岗。...
    茶點故事閱讀 40,928評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡或杠,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出宣蔚,到底是詐尸還是另有隱情向抢,我是刑警寧澤,帶...
    沈念sama閱讀 36,583評論 5 351
  • 正文 年R本政府宣布胚委,位于F島的核電站挟鸠,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏亩冬。R本人自食惡果不足惜兄猩,卻給世界環(huán)境...
    茶點故事閱讀 42,264評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望鉴未。 院中可真熱鬧枢冤,春花似錦、人聲如沸铜秆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,755評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽连茧。三九已至核蘸,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間啸驯,已是汗流浹背客扎。 一陣腳步聲響...
    開封第一講書人閱讀 33,869評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留罚斗,地道東北人徙鱼。 一個月前我還...
    沈念sama閱讀 49,378評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像针姿,于是被迫代替她去往敵國和親袱吆。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,937評論 2 361

推薦閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)自bireme距淫,原地址:https://blog.ibireme.com/2015/05/18/runloop/...
    乜_啊_閱讀 1,388評論 0 5
  • 轉(zhuǎn)載:http://www.cocoachina.com/ios/20150601/11970.html RunL...
    Gatling閱讀 1,443評論 0 13
  • RunLoop 的概念 一般來講绞绒,一個線程一次只能執(zhí)行一個任務(wù),執(zhí)行完成后線程就會退出榕暇。如果我們需要一個機制蓬衡,讓線...
    Mirsiter_魏閱讀 619評論 0 2
  • 前言 RunLoop是iOS和OSX開發(fā)中非秤麒荆基礎(chǔ)的一個概念,這篇文章將從CFRunLoop的源碼入手狰晚,介紹Run...
    暮年古稀ZC閱讀 2,248評論 1 19
  • Runloop是iOS和OSX開發(fā)中非惩彩危基礎(chǔ)的一個概念,從概念開始學(xué)習(xí)家肯。 RunLoop的概念 -般說龄砰,一個線程一...
    小貓仔閱讀 999評論 0 1