常驻线程基本的思路都是等待信号>收到信号>执行回调>继续等待信号,在项目开发中需要将一些耗时的,不紧要的任务放到常驻子线程中处理。
记录两种实现常驻线程的方式:
一、NSThread+RunLoop
- (void)onStart:(UIButton*)sender
{
NSLog(@"start %@",self.navigationItem.title);
[NSThread detachNewThreadSelector:@selector(onThread:) toTarget:self withObject:nil];
}
- (void)onThread:(id)sneder
{
NSLog(@"run ...");
self.workerThread_NS = [NSThread currentThread];
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(onCheckNSTask:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] run];
NSLog(@"over");
}
- (void)onCheckNSTask:(id)sender
{
NSLog(@"checking ..");
}
或者
- (void)onStart:(UIButton*)sender
{
NSLog(@"start %@",self.navigationItem.title);
[NSThread detachNewThreadSelector:@selector(onThread:) toTarget:self withObject:nil];
}
- (void)onThread:(id)sneder
{
self.workerThread_NS = [NSThread currentThread];
// Should keep the runloop from exiting
CFRunLoopSourceContext context = {0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
CFRunLoopSourceRef source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
BOOL runAlways = YES; // Introduced to cheat Static Analyzer
while (runAlways) {
CFRunLoopRun();
}
// Should never be called, but anyway
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
CFRelease(source);
}
二、NSThread+信号量
/**
常驻线程
*/
@interface ResidentThread : NSObject
-(void)doAction:(dispatch_block_t)action;
-(void)cancel;
@end
@implementation ResidentThread{
NSMutableArray *actions;
NSThread *thread;
dispatch_semaphore_t sem;
bool cancel;
}
-(id)init{
self = [super init];
actions = [NSMutableArray array];
// 创建信号量
sem = dispatch_semaphore_create(0);
// 创建一个新线程
thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
[thread start];
return self;
}
-(void)run{
while (true) {
// 等待信号量,信号量减1
dispatch_semaphore_wait(sem, -1);
// 收到信号
// 如果线程已经取消了,那么退出循环
if(cancel){
break;
}
// 开始执行任务
dispatch_block_t block = [actions firstObject];
if(block){
[actions removeObject:block];
block();
}
}
}
// 执行某个任务
-(void)doAction:(dispatch_block_t)action{
if(!cancel){ // 如果线程已经cancel了,那么直接忽略
// 将任务放入数组
[actions addObject:[action copy]];
// 发送信号,信号量加1
dispatch_semaphore_signal(sem);
}
}
// 终止常驻线程
-(void)cancel{
cancel = YES;
// 线程取消后,清空所有的回调
[actions removeAllObjects];
// 相当于发送一个终止任务的信号
dispatch_semaphore_signal(sem);
}
@end
doAction::这个方法接收一个dispatch_block_t的参数,代表实际需要让常驻线程执行的任务。
cancel:终止该常驻线程。
为了安全起见,这里采用的是标记位的方式。另外要注意到,cancel方法里面额外发送了一个信号,这个信号的作用类似于发送了一个终止任务的信号。
网友评论