iOS底层学习-day-18
前言-OC-runloop篇
我是一名iOS开发者, iOS底层 菜鸟的进阶之路30天。
问题
runloop 是怎么响应用户操作的, 具体流程是什么样的?
- 由source1将系统事件捕捉,比如点击屏幕事件
- 如何再包装成事件队列EventQueue
说说runLoop的几种状态
- kCFRunLoopEntry = (1UL << 0),//进入
- kCFRunLoopBeforeTimers = (1UL << 1),//即将进入timer
- kCFRunLoopBeforeSources = (1UL << 2),//即将进入source
- kCFRunLoopBeforeWaiting = (1UL << 5),//即将进入开始
- kCFRunLoopAfterWaiting = (1UL << 6),//已经开始
- kCFRunLoopExit = (1UL << 7),//退出
- kCFRunLoopAllActivities = 0x0FFFFFFFU
runloop的mode作用是什么?
- kCFRunLoopDefaultMode(NSDefaultRunLoopMode):App的默认Mode,通常主线程是在这个Mode下运行
- UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
runloop的应用
runloop处理block
CFRunLoopPerformBlock(<#CFRunLoopRef rl#>, <#CFTypeRef mode#>, <#^(void)block#>)
- RunLoop的运行逻辑
线程间的处理 - 在子线程处理事情回到主线程
dispatch_async(dispatch_get_global_queue(0, 0), ^{
// 处理一些子线程的逻辑
// 回到主线程去刷新UI界面,这个依赖于runloop
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"11111111111");
});
});
runLoop解决NSTimer在滑动时停止工作的问题
static int count = 0;
//在defult下工作,而不在UITracking下工作,一般不用scheduled,因为它直接添加到defult
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
NSLog(@"%d", ++count);
CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());//打印模式
}];
// [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
// [[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
// NSDefaultRunLoopMode、UITrackingRunLoopMode才是真正存在的模式
// NSRunLoopCommonModes并不是一个真的模式,它只是一个标记
// timer能在_commonModes数组中存放的模式下工作,_commonModes装着NSDefaultRunLoopMode,UITrackingRunLoopMode
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
runLoop控制线程生命周期(线程保活)
- 例子1
- MJThread类
#import <Foundation/Foundation.h>
@interface MJThread : NSThread
@end
MJThread.m
#import "MJThread.h"
@implementation MJThread
- (void)dealloc
{
NSLog(@"%s", __func__);
}
@end
- ViewController.m
#import "ViewController.h"
#import "MJThread.h"
@interface ViewController ()
@property (strong, nonatomic) MJThread *thread;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.thread = [[MJThread alloc] initWithTarget:self selector:@selector(run) object:nil];
[self.thread start];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:NO];//waitUntilDone YES 等执行完,再往下走,如果NO,就同时执行NSLog(@"123");
NSLog(@"123");
}
// 子线程需要执行的任务
- (void)test
{
NSLog(@"%s %@", __func__, [NSThread currentThread]);
}
// 这个方法的目的:线程保活
- (void)run {
NSLog(@"%s %@", __func__, [NSThread currentThread]);
// 往RunLoop里面添加Source\Timer\Observer
[[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
NSLog(@"%s ----end----", __func__);
}
@end
- 例子2
- MJThread类
MJThread.h
#import <Foundation/Foundation.h>
@interface MJThread : NSThread
@end
MJThread.m
#import "MJThread.h"
@implementation MJThread
- (void)dealloc {
NSLog(@"%s", __func__);
}
@end
- ViewController.m
#import "ViewController.h"
#import "MJThread.h"
@interface ViewController ()
@property (strong, nonatomic) MJThread *thread;
@property (assign, nonatomic, getter=isStoped) BOOL stopped;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
__weak typeof(self) weakSelf = self;
self.stopped = NO;
self.thread = [[MJThread alloc] initWithBlock:^{
NSLog(@"%@----begin----", [NSThread currentThread]);
// 往RunLoop里面添加Source\Timer\Observer
[[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
while (!weakSelf.isStoped) {
//[NSDate distantFuture] 传入一个遥远的超时的时间
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
NSLog(@"%@----end----", [NSThread currentThread]);
// NSRunLoop的run方法是无法停止的,它专门用于开启一个永不销毁的线程(NSRunLoop)
// [[NSRunLoop currentRunLoop] run];
/*
it runs the receiver in the NSDefaultRunLoopMode by repeatedly invoking runMode:beforeDate:.
In other words, this method effectively begins an infinite loop that processes data from the run loop’s input sources and timers
*/
}];
[self.thread start];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:NO];
}
// 子线程需要执行的任务
- (void)test {
NSLog(@"%s %@", __func__, [NSThread currentThread]);
}
- (IBAction)stop {//点击按钮调用
// 在子线程调用stop
[self performSelector:@selector(stopThread) onThread:self.thread withObject:nil waitUntilDone:NO];
}
// 用于停止子线程的RunLoop
- (void)stopThread {
// 设置标记为NO
self.stopped = YES;
// 停止RunLoop
CFRunLoopStop(CFRunLoopGetCurrent());
NSLog(@"%s %@", __func__, [NSThread currentThread]);
}
- (void)dealloc {
NSLog(@"%s", __func__);
//[self stop];//调用后崩溃,因为waitUntilDone选择NO,那么就是直接走dealloc的方法就直接释放了当前控制器,所以,调用stop中的self的时候,控制器已经被销毁,所以会出现坏内存访问
}
@end
- 例子3 - 真解
- ViewController.m
#import "ViewController.h"
#import "MJThread.h"
@interface ViewController ()
@property (strong, nonatomic) MJThread *thread;
@property (assign, nonatomic, getter=isStoped) BOOL stopped;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
__weak typeof(self) weakSelf = self;
self.stopped = NO;
self.thread = [[MJThread alloc] initWithBlock:^{
NSLog(@"%@----begin----", [NSThread currentThread]);
// 往RunLoop里面添加Source\Timer\Observer
[[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
while (weakSelf && !weakSelf.isStoped) {//weakSelf因为它指向的对象要被销毁了。那么它自己就置空了
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
NSLog(@"%@----end----", [NSThread currentThread]);
}];
[self.thread start];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
if (!self.thread) return;
[self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:NO];
}
// 子线程需要执行的任务
- (void)test
{
NSLog(@"%s %@", __func__, [NSThread currentThread]);
}
- (IBAction)stop {
if (!self.thread) return;
// 在子线程调用stop(waitUntilDone设置为YES,代表子线程的代码执行完毕后,这个方法才会往下走)
[self performSelector:@selector(stopThread) onThread:self.thread withObject:nil waitUntilDone:YES];
}
// 用于停止子线程的RunLoop
- (void)stopThread {
// 设置标记为YES
self.stopped = YES;
// 停止RunLoop
CFRunLoopStop(CFRunLoopGetCurrent());
NSLog(@"%s %@", __func__, [NSThread currentThread]);
// 清空线程
self.thread = nil;
}
- (void)dealloc {
NSLog(@"%s", __func__);
[self stop];
}
@end