iOS中默認就有個主線程即mainThread祖能,我們的UI線程指的就是主線程媚媒,一般都是在主線程中操作UI搏色,從某個角度來講,我們的主線程就是一個常駐線程捏肢。
一般情況下,我們開啟一個子線程饥侵,讓子線程去跑一個任務鸵赫,當任務執(zhí)行完畢之后,該線程就會被系統(tǒng)自動銷毀躏升。假如說 在一個遍歷循環(huán)中辩棒,要執(zhí)行10000次創(chuàng)建子線程去執(zhí)行一個耗時任務,那么這么頻繁的去創(chuàng)建和銷毀線程就會造成資源的浪費膨疏,那我們?yōu)槭裁床蛔岊l繁使用的子線程常駐在內(nèi)存中一睁,想用的時候就用,不想用的時候讓他休眠呢成肘?
接下來就示例創(chuàng)建一個常駐線程卖局。
- 首先創(chuàng)建一個ThreadManager類,為類添加一個暴露類屬性双霍,一個隱私類屬性:
@interface ThreadManager : NSObject
@property (nonatomic, assign, class) BOOL isShouldKeepRunning;
@end
@interface ThreadManager()
@property (nonatomic, strong, class) NSThread * residentThread;
@end
其中isShouldKeepRuning屬性可以用來控制當前子線程中runloop是否停止
residentThread為要創(chuàng)建引用的常駐線程
- 接下來 聲明一個靜態(tài)變量砚偶,開辟空間
static BOOL _isShouldKeepRuning;
static NSThread * _residentThread;
為其提供getter和setter方法
+ (BOOL)isShouldKeepRunning {
return _isShouldKeepRuning;
}
+ (void)setIsShouldKeepRunning:(BOOL)isShouldKeepRunning {
_isShouldKeepRuning = isShouldKeepRunning;
}
+ (NSThread *)residentThread {
if (_residentThread == nil) {
// thread方法為創(chuàng)建線程的方法,下面會寫到洒闸。
_residentThread = [self thread];
}
return _residentThread;
}
+ (void)setResidentThread:(NSThread *)residentThread {
_residentThread = residentThread;
}
提供線程創(chuàng)建的方法 以及在子線程中獲取runloop
+ (NSThread *)thread {
NSThread * thread = nil;
__weak typeof(self)weakSelf = self;
void(^creatThreadBlock)(void) = ^{
NSRunLoop * currentLoop = [NSRunLoop currentRunLoop];
[currentLoop addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
while (weakSelf && weakSelf.isShouldKeepRunning) {
[currentLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
NSLog(@"runloop 停止染坯! ! 丘逸!");
};
self.isShouldKeepRunning = YES;
if (@available(iOS 10.0, *)) {
thread = [[NSThread alloc] initWithTarget:self selector:@selector(creatThreadMethod:) object:creatThreadBlock];
} else {
thread = [[NSThread alloc] initWithBlock:creatThreadBlock];
}
thread.name = @"thread_resident";
[thread start];
return thread;
}
+ (void)creatThreadMethod:(void(^)(void))task {
if (task) {
task();
}
}
+ (void)executeTask:(void(^)(void))task {
[self performSelector:@selector(threadTaskMethod:) onThread:self.residentThread withObject:task waitUntilDone:NO];
}
+ (void)threadTaskMethod:(void(^)(void))task {
if (task) {
NSLog(@"currentThread: %@", [NSThread currentThread]);
task();
}
}
其中
NSRunLoop * currentLoop = [NSRunLoop currentRunLoop];
[currentLoop addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
- RunLoop只能獲取单鹿,不能創(chuàng)建。 在子線程中深纲,獲取runloop仲锄,如果沒有, 系統(tǒng)會為我們創(chuàng)建湃鹊,如果currentLoop中沒有 source0事件儒喊、source1事件、定時器币呵、obsever 這些事件資源怀愧,那么RunLoop不會循環(huán),會直接結束,所以要給RunLoop加一個監(jiān)聽端口芯义,用來監(jiān)聽系統(tǒng)事件的發(fā)生哈垢,那么這樣子才能夠保證RunLoop一直循環(huán)不退出。
- 最后將
+ (void)executeTask:(void(^)(void))task;
方法暴露出來扛拨,外部就可以直接拿來使用耘分。下面是我測試的代碼和測試結果
[ThreadManager executeTask:^{
NSLog(@"1 1 1 子線程 執(zhí)行操作 ");
}];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[ThreadManager executeTask:^{
NSLog(@"2 2 2 子線程 執(zhí)行操作 ");
}];
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[ThreadManager executeTask:^{
NSLog(@"3 3 3 子線程 執(zhí)行操作 ");
}];
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(6.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
ThreadManager.isShouldKeepRunning = NO;
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(8.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[ThreadManager executeTask:^{
NSLog(@"4 4 4 子線程 執(zhí)行操作 ");
}];
});
結果如下:
- 可見任務的執(zhí)行都在同一個子線程當中,當改變
isShouldKeepRunning
的值鬼癣,在下一次RunLoop循環(huán)的時候陶贼,RunLoop循環(huán)被打破,退出RunLoop待秃,此時常駐線程也不復存在。 - 后續(xù)也可以再擴展一個方法用來重新掉起一條新的常駐線程痹屹,那么可以達到在某些情況下章郁,已經(jīng)執(zhí)行了很多任務,用不到這個線程志衍,不想讓這個線程在內(nèi)存中存在暖庄,手動給銷毀掉,在用到的時候 重新創(chuàng)建楼肪。