iOS NSThread 源码分析原理,及如何实现常驻线程
- (void) start
{
pthread_attr_t attr;
if (_active == YES)
{
[NSException raise: NSInternalInconsistencyException
format: @"[%@-%@] called on active thread",
NSStringFromClass([self class]),
NSStringFromSelector(_cmd)];
}
if (_cancelled == YES)
{
[NSException raise: NSInternalInconsistencyException
format: @"[%@-%@] called on cancelled thread",
NSStringFromClass([self class]),
NSStringFromSelector(_cmd)];
}
if (_finished == YES)
{
[NSException raise: NSInternalInconsistencyException
format: @"[%@-%@] called on finished thread",
NSStringFromClass([self class]),
NSStringFromSelector(_cmd)];
}
/* Make sure the notification is posted BEFORE the new thread starts.
*/
gnustep_base_thread_callback();
/* The thread must persist until it finishes executing.
*/
RETAIN(self);
/* Mark the thread as active while it's running.
*/
_active = YES;
errno = 0;
pthread_attr_init(&attr);
/* Create this thread detached, because we never use the return state from
* threads.
*/
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
/* Set the stack size when the thread is created. Unlike the old setrlimit
* code, this actually works.
*/
if (_stackSize > 0)
{
pthread_attr_setstacksize(&attr, _stackSize);
}
if (pthread_create(&pthreadID, &attr, nsthreadLauncher, self))
{
DESTROY(self);
[NSException raise: NSInternalInconsistencyException
format: @"Unable to detach thread (last error %@)",
[NSError _last]];
}
}
从上面代码可以看出,前面也是一些异常判断,然后紧接着会创建一个pthread,pthread_create
,同时会设置一个启动函数nsthreadLauncher
static void *
nsthreadLauncher(void *thread)
{
NSThread *t = (NSThread*)thread;
setThreadForCurrentThread(t);
/*
* Let observers know a new thread is starting.
*/
if (nc == nil)
{
nc = RETAIN([NSNotificationCenter defaultCenter]);
}
[nc postNotificationName: NSThreadDidStartNotification
object: t
userInfo: nil];
[t _setName: [t name]];
[t main];
[NSThread exit];
// Not reached
return NULL;
}
然后我们分析这个启动函数,
会发现系统为什么发送了一个通知NSThreadDidStartNotification,然后设置名字,紧接着调用main函数,当我们的main函数调用完成之后,就会调用exit,退出,所以如果我们想实现常驻线程,只需要对main函数做手脚就可以了,那么main函数是怎么实现的呢?
- (void) main
{
if (_active == NO)
{
[NSException raise: NSInternalInconsistencyException
format: @"[%@-%@] called on inactive thread",
NSStringFromClass([self class]),
NSStringFromSelector(_cmd)];
}
[_target performSelector: _selector withObject: _arg];
}
会发现最后利用了 [_target performSelector: _selector withObject: _arg];
这个方法来调用我们的selector,这个就是我们传进来的selector,所以如果我们想实现常驻线程,只需要在我们穿进去的selector做手脚就可以实现了,是不是瞬间豁然开朗?
练习
- (void)viewDidLoad {
[super viewDidLoad];
_queue = dispatch_queue_create("com.rongcloud.sunchengxiu", DISPATCH_QUEUE_CONCURRENT);
_dic = [NSMutableDictionary dictionary];
// [self testGroup1];
[self testThread];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self performSelector:@selector(print) onThread:_thread withObject:nil waitUntilDone:NO];
}
- (void)testThread{
_thread=[[NSThread alloc] initWithTarget:self selector:@selector(print) object:nil];
[_thread start];
}
会发现当我们touch的时候,方法没有调用,是因为在源码内部调用了exit,所以我们要实现常驻线程,经过改造
- (void)viewDidLoad {
[super viewDidLoad];
_queue = dispatch_queue_create("com.rongcloud.sunchengxiu", DISPATCH_QUEUE_CONCURRENT);
_dic = [NSMutableDictionary dictionary];
// [self testGroup1];
[self testThread];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self performSelector:@selector(print) onThread:_thread withObject:nil waitUntilDone:NO];
}
- (void)testThread{
_thread=[[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
[_thread start];
}
- (void)run{
[[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop]run];
}
- (void)print{
NSLog(@"%@",[NSThread currentThread]);
}
这样就可以实现了,常驻线程.
网友评论