iOS中默认就有个主线程即mainThread,我们的UI线程指的就是主线程,一般都是在主线程中操作UI,从某个角度来讲,我们的主线程就是一个常驻线程。
一般情况下,我们开启一个子线程,让子线程去跑一个任务,当任务执行完毕之后,该线程就会被系统自动销毁。假如说 在一个遍历循环中,要执行10000次创建子线程去执行一个耗时任务,那么这么频繁的去创建和销毁线程就会造成资源的浪费,那我们为什么不让频繁使用的子线程常驻在内存中,想用的时候就用,不想用的时候让他休眠呢?
接下来就示例创建一个常驻线程。
- 首先创建一个ThreadManager类,为类添加一个暴露类属性,一个隐私类属性:
@interface ThreadManager : NSObject
@property (nonatomic, assign, class) BOOL isShouldKeepRunning;
@end
@interface ThreadManager()
@property (nonatomic, strong, class) NSThread * residentThread;
@end
其中isShouldKeepRuning属性可以用来控制当前子线程中runloop是否停止
residentThread为要创建引用的常驻线程
- 接下来 声明一个静态变量,开辟空间
static BOOL _isShouldKeepRuning;
static NSThread * _residentThread;
为其提供getter和setter方法
+ (BOOL)isShouldKeepRunning {
return _isShouldKeepRuning;
}
+ (void)setIsShouldKeepRunning:(BOOL)isShouldKeepRunning {
_isShouldKeepRuning = isShouldKeepRunning;
}
+ (NSThread *)residentThread {
if (_residentThread == nil) {
// thread方法为创建线程的方法,下面会写到。
_residentThread = [self thread];
}
return _residentThread;
}
+ (void)setResidentThread:(NSThread *)residentThread {
_residentThread = residentThread;
}
提供线程创建的方法 以及在子线程中获取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只能获取,不能创建。 在子线程中,获取runloop,如果没有, 系统会为我们创建,如果currentLoop中没有 source0事件、source1事件、定时器、obsever 这些事件资源,那么RunLoop不会循环,会直接结束,所以要给RunLoop加一个监听端口,用来监听系统事件的发生,那么这样子才能够保证RunLoop一直循环不退出。
- 最后将
+ (void)executeTask:(void(^)(void))task;
方法暴露出来,外部就可以直接拿来使用。下面是我测试的代码和测试结果
[ThreadManager executeTask:^{
NSLog(@"1 1 1 子线程 执行操作 ");
}];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[ThreadManager executeTask:^{
NSLog(@"2 2 2 子线程 执行操作 ");
}];
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[ThreadManager executeTask:^{
NSLog(@"3 3 3 子线程 执行操作 ");
}];
});
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 子线程 执行操作 ");
}];
});
结果如下:
- 可见任务的执行都在同一个子线程当中,当改变
isShouldKeepRunning
的值,在下一次RunLoop循环的时候,RunLoop循环被打破,退出RunLoop,此时常驻线程也不复存在。 - 后续也可以再扩展一个方法用来重新掉起一条新的常驻线程,那么可以达到在某些情况下,已经执行了很多任务,用不到这个线程,不想让这个线程在内存中存在,手动给销毁掉,在用到的时候 重新创建。