在我們需要頻繁地開啟子線程執(zhí)行操作的時候芜果,我們可以采用開啟子線程runloop的方式本铣剩活子線程,這樣避免頻繁創(chuàng)建線程銷毀線程的開銷右钾。
具體的币狭撸活方式.
@interface ZLPermanentThread : NSObject
/** 在當前子線程執(zhí)行一個任務(wù)*/
- (void)executeTask:(void(^)(void))task;
/**結(jié)束線程*/
- (void)stop;
@end
@interface ZLPermanentThread()
@property (nonatomic, strong) NSThread *thread;
//@property (nonatomic, assign, getter=isStopped) BOOL stopped;
@end
@implementation ZLPermanentThread
- (instancetype)init{
if (self = [super init]) {
// self.stopped = NO;
// __weak typeof(self) weakSelf = self;
self.thread = [[NSThread alloc] initWithBlock:^{// ios 10之后才有這個方法
CFRunLoopSourceContext context = {0};
CFRunLoopSourceRef source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
CFRelease(source);
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e10, false);
NSLog(@"runloop end---");
// NSRunLoop *currentRunloop = [NSRunLoop currentRunLoop];
// [currentRunloop addPort:[[NSPort alloc]init] forMode:NSDefaultRunLoopMode];
// while (weakSelf && !weakSelf.isStopped) {
// [currentRunloop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
// }
}];
[self.thread start];
}
return self;
}
- (void)__executeTask:(void (^)(void))task{
task();
}
- (void)executeTask:(void (^)(void))task{
if (!self.thread || !task) return;
[self performSelector:@selector(__executeTask:) onThread:self.thread withObject:task waitUntilDone:NO];
}
- (void)stop{
if (!self.thread) return;
[self performSelector:@selector(__stop) onThread:self.thread withObject:nil waitUntilDone:YES];
}
- (void)__stop{
// self.stopped = YES;
CFRunLoopStop(CFRunLoopGetCurrent());
self.thread = nil;
}
- (void)dealloc{
NSLog(@"%s", __func__);
[self stop];
}
@end
使用時
// 初始化
self.thread = [[ZLPermanentThread alloc] init];
// 執(zhí)行任務(wù)
[self.thread executeTask:^{
NSLog(@"子線程執(zhí)行了任務(wù)");
}];
關(guān)鍵點:
- 子線程的runloop需要手動去調(diào)用運行。
- 子線程runloop手動運行成功開啟循環(huán)需要先在runloop中添加事件源或者timer舀射。
- CFRunLoopStop(CFRunLoopGetCurrent());可以停止runloop運行窘茁。
附:Runloop相關(guān)知識點
1. Runloop與線程的關(guān)系尘分?
Runloop是一個運行循環(huán),與線程是一一對應(yīng)的關(guān)系描滔,線程中有runloop或者自己定義的循環(huán)運行時笙僚,子線程就不會被銷毀,主線程中Runloop是默認開啟的慎王;runloop是系統(tǒng)提供的運行循環(huán),相比自己手動寫while循環(huán),有以下幾個優(yōu)點:
- 能處理APP的input souce事件张抄、定時器事件、代碼執(zhí)行的事件洼怔。
- 在沒有事件要處理時會進入休眠(系統(tǒng)層面的休眠函數(shù))署惯,節(jié)省CPU資源。
下面的runloop的本質(zhì)結(jié)構(gòu):
// 下載最大版本號的源碼壓縮包镣隶,找到__CFRunLoop.
// 下面是簡化了一些變量后的__CFRunLoop
struct __CFRunLoop {
pthread_t _pthread;
CFMutableSetRef _commonModes; // 共同的模式极谊,相當于NSRunLoopCommonModes
CFMutableSetRef _commonModeItems;// 共同的模式下的Item
CFRunLoopModeRef _currentMode;
CFMutableSetRef _modes;
};
// 下面是簡化了一些變量后的__CFRunLoopMode
struct __CFRunLoopMode {
CFStringRef _name;
CFMutableSetRef _sources0;
CFMutableSetRef _sources1;
CFMutableArrayRef _observers;
CFMutableArrayRef _timers;
};
有幾件事情需要說明一下:
1.
runloop
同一時間只能處于一個RunLoopMode
下.
- 系統(tǒng)提供每個
RunLoopMode
下會有自己的Item
,這個item可能是source
,timer
,observer
; 自定義的RunLoopMode
安岂,自定義的RunLoopMode
需要自己往里面添加Item轻猖。- 如果一個runloop中沒有任何一個item,那么runloop會立即停止域那。
2. Runloop有幾種模式咙边?
官網(wǎng)顯示的是5種,我們需要關(guān)心的就是:Default模式、tracking模式败许、Common模式王带。NSRunloop簡單細說(六)—— 幾種循環(huán)模式詳細解析
3. Runloop有幾種狀態(tài)?
7種狀態(tài):即將進入市殷、即將處理timer愕撰、即將處理source、即將進入休眠醋寝、從休眠中喚醒盟戏、退出runloop、AllActivities甥桂。
/* Run Loop Observer Activities */
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0),
kCFRunLoopBeforeTimers = (1UL << 1),
kCFRunLoopBeforeSources = (1UL << 2),
kCFRunLoopBeforeWaiting = (1UL << 5),
kCFRunLoopAfterWaiting = (1UL << 6),
kCFRunLoopExit = (1UL << 7),
kCFRunLoopAllActivities = 0x0FFFFFFFU
};
4. Runloop是怎么運行的
Runloop運行的時候是開啟了一個do..while循環(huán)柿究,執(zhí)行一個循環(huán)時首先處理完Observer、Source0黄选、Source1蝇摸、timer,然后調(diào)用系統(tǒng)的休眠函數(shù)等待被喚醒办陷,最后在喚醒時繼續(xù)處理事件再次進入循環(huán)貌夕。 Runloop的內(nèi)部結(jié)構(gòu)與運行原理
5. Runloop的一些運用?
- 使用NSTimer民镜、CADisplayLink時需要addTimer:forMode:啡专, 這個時候如果不希望滾動視圖時停止timer就需要傳入
NSRunLoopCommonModes
. - 在子線程中使用
- (void)performSelector: withObject:afterDelay:
時方法不會得到執(zhí)行,原因就是子線程runloop未開啟而這個方法的實現(xiàn)是開啟timer將timer加到了runloop制圈。 - 常駐子線程見上面的實現(xiàn)们童。
- iOS系統(tǒng)對主線程Runloop的狀態(tài)進行了監(jiān)聽
addObserver:
,在runloop開啟時創(chuàng)建了一個autoreleasepool
鲸鹦,在進入休眠時銷毀這個autoreleasepool
然后創(chuàng)建一個新的autoreleasepool
慧库,在runloop退出時銷毀autoreleasepool
,這樣完成了自動的對對象內(nèi)存的管理馋嗜。
推薦閱讀:
博客:NSRunloop簡單細說(六)—— 幾種循環(huán)模式詳細解析
官方文檔:Runloop官方文檔
源碼:CF框架源碼