在RunLoop 二 : RunLoop在實際中的應用篇幅中我們介紹了runloop
在項目中的具體用法,并且創(chuàng)建了一個可以控制生命周期的線程.今天我們就用OC
和C語言
兩種方法封裝一個線程笨曷牛活的工具類:
一:使用OC
語言封裝:
// 監(jiān)控線程生命周期
@interface MYThread : NSThread
@end
@implementation MYThread
- (void)dealloc{
NSLog(@"%s",__func__);
}
@end
@interface HHSaveLifeThread ()
@property (nonatomic,strong)MYThread *thread;
/// 控制runloop是否停止循環(huán)
@property (nonatomic,assign)BOOL isStop;
@end
@implementation HHSaveLifeThread
- (instancetype)init{
if (self = [super init]) {
//默認runloop不停止,一直循環(huán)
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;
}
// 執(zhí)行任務
- (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 循環(huán)
- (void)__stopRunloop{
self.isStop = YES;
CFRunLoopStop(CFRunLoopGetCurrent());
self.thread= nil;
}
- (void)__executeTaskWithBlock:(void (^)(void))taskBlock{
taskBlock();
}
點擊這里下載OC版本demo.
二:使用C語言
封裝:
// 監(jiān)控線程生命周期
@interface MYThread : NSThread
@end
@implementation MYThread
- (void)dealloc{
NSLog(@"%s",__func__);
}
@end
@interface HHSaveLifeThread ()
@property (nonatomic,strong)MYThread *thread;
/// 控制runloop是否停止循環(huán)
@property (nonatomic,assign)BOOL isStop;
@end
@implementation HHSaveLifeThread
- (instancetype)init{
if (self = [super init]) {
//默認runloop不停止,一直循環(huán)
self.isStop = NO;
__weak typeof(self) weakSelf = self;
self.thread = [[MYThread alloc]initWithBlock:^{
//保住此線程的命,獲取當前線程的 runloop ,添加任務
NSLog(@"-------------start-------------");
//創(chuàng)建一個上下文環(huán)境
CFRunLoopSourceContext context = {0};
//創(chuàng)建一個source源
CFRunLoopSourceRef source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
//runloop添加source源
CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
// 最后一個參數(shù)如果為 true : 循環(huán)一次后就退出 , 為 false ,不退出
// while (weakSelf && !weakSelf.isStop) {
//啟動runloop
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1E30, false);
// }
NSLog(@"-------------end-------------");
}];
[self.thread start];
}
return self;
}
// 執(zhí)行任務
- (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 循環(huán)
- (void)__stopRunloop{
// self.isStop = YES;
CFRunLoopStop(CFRunLoopGetCurrent());
self.thread= nil;
}
- (void)__executeTaskWithBlock:(void (^)(void))taskBlock{
taskBlock();
}
點擊這里下載C語言版本demo.
思考:
1. 封裝此工具為什么給 NSThread 添加分類這種方式不太好?
答:因為如果使用分類的話,給分類添加屬性比較麻煩,需要用到 associate 關聯(lián)對象這種技術.
2. 為什么不采用繼承自 NSThread 這種方式?
答:直接繼承自 NSThread 這種方式不太安全,因為 NSThread 類中暴露了很多直接操作 線程 的 API,比如 start,cancle,stop等,其他使用此工具的開發(fā)者可能會調動 NSThread
中的方法 打亂 此工具的生命周期.這樣的話我們也無法保證線程的聲明周期,所以我們繼承自 NSObject 更好一些.把線程操作相關的方法封裝起來,更安全.