RunLoop详解
runloop的本质是一个对象,这个对象有一个入口函数,执行入口函数之后就会进入一个do while循环,循环的处理一些事情。
没有runloop的情况下,程序运行完成就会退出。
有runloop的时候程序运行完成的时候不会退出
runloop的作用
- 保持程序持续运行
- 处理app中的各种事件(触摸、定时器、performselector等)
- 节省cpu资源,提高程序性能(有事件的饿时候处理事件,没事件的时候休息,可以在main函数中直接返回1进行测试)
线程和runloop
- 线程和runloop是一一对应的关系(内部是一个字典,key为线程,value为runloop)
- 线程创建的时候,并没有创建runloop对象,runloop会在第一次获取的时候自动创建
- 主线程默认开启了runloop,子线程默认没有开启runloop
runloop的mode
- 一个runloop对应一个线程
- 一个runloop包含多个mode,每个mode包含若干个source、timer、observer
- source、timer、observer又叫modeitem,不同的mode下mode item 互不影响
- runloop运行过程中,只会选择一种模式运行
- 切换mode,程序退出当前mode,再重新制定mode执行
ModeItem
source0事件
- 触摸事件
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
NSLog(@"testRunloop"); //打断点可以看到堆栈信息中 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__
}
- 自定义输入园source0
- (void)testAction:(UIButton *)sender
{
NSThread *subThread = [[NSThread alloc]initWithBlock:^{
CFRunLoopSourceContext context = {
0,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
schedule,
cancel,
perform
};
CFRunLoopSourceRef source0 = CFRunLoopSourceCreate(CFAllocatorGetDefault(), 0, &context);
//有下面一行才能触发schedule回调
CFRunLoopAddSource(CFRunLoopGetCurrent(), source0, kCFRunLoopDefaultMode);
//有以下代码才能触发perform回调
CFRunLoopSourceSignal(source0);
CFRunLoopWakeUp(CFRunLoopGetCurrent());
//触发cancel回调
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), source0, kCFRunLoopDefaultMode);
CFRelease(source0); //释放
}];
[subThread start];
}
void schedule(void *info, CFRunLoopRef rl, CFRunLoopMode mode)
{
NSLog(@"currentThreed ===%s",__func__);
}
void cancel(void *info, CFRunLoopRef rl, CFRunLoopMode mode)
{
NSLog(@"currentThreedMode ===%s",__func__);
}
void perform(void *info)
{
NSLog(@"currentThreedInfo ===%s",__func__);
}
- performSelector:onThread:方法调用
[self performSelectorOnMainThread:@selector(animation:) withObject:nil waitUntilDone:NO];
- (void)animation:(id)sender
{
NSLog(@"哈哈"); //此处打断点可以在堆栈中看到 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__
}
source1
- 端口(NSPort)----
注意测试代码在模拟器上控制台能打印出来在手机上不行,原因是通过NSPort的port类方法创建出来的是它的子类对象NSMachPort,而Foundation框架给我们提供的NSPort的三个子类中NSMachPort和NSMessagePort只能用于同一台机器上的通信,NSSocketPort可以用于本地和远程通信。
- (void)viewDidLoad
{
[super viewDidLoad];
self.mainPort = [NSPort port];
self.mainPort.delegate = self;
[[NSRunLoop currentRunLoop] addPort:self.mainPort forMode:NSDefaultRunLoopMode];
NSThread *testThread = [[NSThread alloc]initWithBlock:^{
self.subPort = [NSPort port];
self.subPort.delegate = self;
[[NSRunLoop currentRunLoop] addPort:self.subPort forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
}];
[testThread setName:@"subThread"];
[testThread start];
}
- (IBAction)testAction:(UIButton *)sender
{
NSMutableArray *conmponents = [NSMutableArray array];
[conmponents addObject:[@"fromMainThread" dataUsingEncoding:NSUTF8StringEncoding]];
[self.subPort sendBeforeDate:[NSDate date] components:conmponents from:self.mainPort reserved:0];
}
- (void)handlePortMessage:(id)message
{
NSMutableArray *array = [message valueForKey:@"components"]; //此处打断点可以在堆栈中看到__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__说明这是一个source1源
NSString *password = [[NSString alloc]initWithData:[array firstObject] encoding:NSUTF8StringEncoding];
NSLog(@"thread ===%@\tpassword===%@", [NSThread currentThread], password);
if (![[NSThread currentThread] isMainThread]) {
NSMutableArray *conmponents = [NSMutableArray array];
[conmponents addObject:[@"fromSubthread" dataUsingEncoding:NSUTF8StringEncoding]];
[self.mainPort sendBeforeDate:[NSDate date] components:conmponents from:self.subPort reserved:0];
}
}
计时源
- NStimer
- performSelector:withObject:afterDelay
[self performSelector:@selector(animation:) withObject:nil afterDelay:3];
- (void)animation:(id)sender
{
NSLog(@"哈哈"); //此处打断点可以在堆栈中看到 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__
}
网友评论