在RunLoop 二 : RunLoop在实际中的应用篇幅中我们介绍了runloop
在项目中的具体用法,并且创建了一个可以控制生命周期的线程.今天我们就用OC
和C语言
两种方法封装一个线程保活的工具类:
一:使用OC
语言封装:
// 监控线程生命周期
@interface MYThread : NSThread
@end
@implementation MYThread
- (void)dealloc{
NSLog(@"%s",__func__);
}
@end
@interface HHSaveLifeThread ()
@property (nonatomic,strong)MYThread *thread;
/// 控制runloop是否停止循环
@property (nonatomic,assign)BOOL isStop;
@end
@implementation HHSaveLifeThread
- (instancetype)init{
if (self = [super init]) {
//默认runloop不停止,一直循环
self.isStop = NO;
__weak typeof(self) weakSelf = self;
self.thread = [[MYThread alloc]initWithBlock:^{
//保住此线程的命,获取当前线程的 runloop ,添加任务
NSPort *port = [[NSPort alloc]init];
[[NSRunLoop currentRunLoop]addPort:port forMode:NSDefaultRunLoopMode];
while (weakSelf && !weakSelf.isStop) {
[[NSRunLoop currentRunLoop]runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
}];
[self.thread start];
}
return self;
}
// 执行任务
- (void)executeTaskWithBlock:(void (^)(void))taskBlock{
if(!self.thread || !taskBlock) return;
[self performSelector:@selector(__executeTaskWithBlock:) onThread:self.thread withObject:taskBlock waitUntilDone:NO];
}
// 停止
- (void)stop{
if (!self.thread) return;
[self performSelector:@selector(__stopRunloop) onThread:self.thread withObject:nil waitUntilDone:YES];
}
- (void)dealloc{
[self stop];
NSLog(@"%s",__func__);
}
#pragma mark 私有api
/// 停止 runloop 循环
- (void)__stopRunloop{
self.isStop = YES;
CFRunLoopStop(CFRunLoopGetCurrent());
self.thread= nil;
}
- (void)__executeTaskWithBlock:(void (^)(void))taskBlock{
taskBlock();
}
点击这里下载OC版本demo.
二:使用C语言
封装:
// 监控线程生命周期
@interface MYThread : NSThread
@end
@implementation MYThread
- (void)dealloc{
NSLog(@"%s",__func__);
}
@end
@interface HHSaveLifeThread ()
@property (nonatomic,strong)MYThread *thread;
/// 控制runloop是否停止循环
@property (nonatomic,assign)BOOL isStop;
@end
@implementation HHSaveLifeThread
- (instancetype)init{
if (self = [super init]) {
//默认runloop不停止,一直循环
self.isStop = NO;
__weak typeof(self) weakSelf = self;
self.thread = [[MYThread alloc]initWithBlock:^{
//保住此线程的命,获取当前线程的 runloop ,添加任务
NSLog(@"-------------start-------------");
//创建一个上下文环境
CFRunLoopSourceContext context = {0};
//创建一个source源
CFRunLoopSourceRef source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
//runloop添加source源
CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
// 最后一个参数如果为 true : 循环一次后就退出 , 为 false ,不退出
// while (weakSelf && !weakSelf.isStop) {
//启动runloop
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1E30, false);
// }
NSLog(@"-------------end-------------");
}];
[self.thread start];
}
return self;
}
// 执行任务
- (void)executeTaskWithBlock:(void (^)(void))taskBlock{
if(!self.thread || !taskBlock) return;
[self performSelector:@selector(__executeTaskWithBlock:) onThread:self.thread withObject:taskBlock waitUntilDone:NO];
}
// 停止
- (void)stop{
if (!self.thread) return;
[self performSelector:@selector(__stopRunloop) onThread:self.thread withObject:nil waitUntilDone:YES];
}
- (void)dealloc{
[self stop];
NSLog(@"%s",__func__);
}
#pragma mark 私有api
/// 停止 runloop 循环
- (void)__stopRunloop{
// self.isStop = YES;
CFRunLoopStop(CFRunLoopGetCurrent());
self.thread= nil;
}
- (void)__executeTaskWithBlock:(void (^)(void))taskBlock{
taskBlock();
}
点击这里下载C语言版本demo.
思考:
1. 封装此工具为什么给 NSThread 添加分类这种方式不太好?
答:因为如果使用分类的话,给分类添加属性比较麻烦,需要用到 associate 关联对象这种技术.
2. 为什么不采用继承自 NSThread 这种方式?
答:直接继承自 NSThread 这种方式不太安全,因为 NSThread 类中暴露了很多直接操作 线程 的 API,比如 start,cancle,stop等,其他使用此工具的开发者可能会调动 NSThread
中的方法 打乱 此工具的生命周期.这样的话我们也无法保证线程的声明周期,所以我们继承自 NSObject 更好一些.把线程操作相关的方法封装起来,更安全.