从字面意思来看:跑圈、运动循环 基本用法:保持程序持续运行、处理App中的各种事件(触摸事件、定时器事件、SEL等等) 为什么需要它:节省CPU资源、 提高性能
如果没有RunLoop,程序在执行到7行就结束了。int main(int argc, const char * argv[]) { @autoreleasepool { // insert code here... NSLog(@"Hello, World!"); } return 0; }有Runloop后,程序就相当于一直在做循环在这个循环内部不断地处理各种任务(比如Source、Timer、Observer)
int main(int argc, const char * argv[]) { BOOL running = YES; do{ // 执行各种任务,处理各种时间 }while (running); return 0; }程序中的Runloop----UIApplicationMainint main(int argc, char *argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } }1.在UIApplicationMain函数内部就启动了一个RunLoop 2.UIApplicationMain函数一直没有返回,保持了程序的持续运行 3.这个默认启动的RunLoop是主线程关联的。 4.一个线程对应一个RunLoop,主线程的RunLoop默认已经启动 5.子线程的RunLoop得手动启动(调用run方法) 6.RunLoop只能选择一个Mode启动,如果当前Mode中没有任Source、Timer、Observer,那么就直接退出RunLoop
RunLoop里面有两套api用来访问和使用RunLoop 1、Foundation--NSRunLoop 2、Core Foundation --- CFRunloopRef 二者异同点: NSRunLoop和CFRunloopRef都代表RunLoop对象,NSRunLoop是对CFRunloopRef一层OC的封装
RunLoop与线程:每条线程都有一个RunLoop对象,主线程默认已经创建好了,子线程需要主动创建 Runloop在第一次获取时创建,在线程结束后销毁。
/* 1. Foundation */ // 获取当前线程 NSRunLoop *roop = [NSRunLoop currentRunLoop]; // 获取主线程 [NSRunLoop mainRunLoop]; /*2. Core Foundation */ // 获取当期线程 CFRunLoopGetCurrent(); // 获取主线程 CFRunLoopGetMain();RunLoop相关类(Runloop中如果没有Source,Observre,Timer,Mode,就会结束) // Runloop对象 CFRunLoopRef; // Runloop事件源(数据源) CFRunLoopSourceRef; // Runloop观察者 CFRunLoopObserverRef; // Runloop时间源 CFRunLoopTimerRef; // Runloop模式 CFRunloopModeRef;Runloop里面相关类的互相关系-Paste_Image.png
// 获取当前Runloop的模式 NSString *runloopMode = [NSRunLoop currentRunLoop].currentMode;1.同一时间只可以运行其中的一种model 2.切换Model只能退出Runloop,重新进入 3.系统默认注册了5个Mode
//App默认的Mode,通常主线程是在这个Mode下运行 NSDefaultRunLoopMode: //界面追踪Mode,用于ScrollView追踪触摸滑动,保证界面滑动时不受其他Mode影响 UITrackingRunLoopMode //这是一个占位用的Mode,不是一种正真的Mode NSRunLoopCommonModes //在刚启动App进入的第一个Mode,启动完成以后就不再使用 UIInitializationRunLoopMode //接收系统事件的内部Mode,通常用不到 GSEventReceiveRunLoopModeCFRunLoopTimerRefCFRunLoopTimerRef是基于时间的触发器
CFRunLoopTimerRef基本上说就是NSTimer,它受RunLoop的Mode影响
NSTimer *time = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(test) userInfo:nil repeats:YES]; // 定时器只运行在NSDefaultRunLoopMode下,一旦RunLoop进入其他模式,这个定时器就不会工作 [[NSRunLoop currentRunLoop] addTimer:time forMode:NSDefaultRunLoopMode]; // 定时器会跑在标记为common modes的模式下 // 标记为common modes的模式:包含:UITrackingRunLoopMode和kCFRunLoopDefaultMode [[NSRunLoop currentRunLoop] addTimer:time forMode:NSRunLoopCommonModes];GCD不受RunLoop的Mode影响 // 获取一个全局并发队列 /* #define DISPATCH_QUEUE_PRIORITY_HIGH 2 #define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 默认 #define DISPATCH_QUEUE_PRIORITY_LOW (-2) #define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN */ dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 1. 设置定时器 // dispatchQueue :决定了将来回调的方法在哪里执行。 // dispatch_source_t timer 是一个OC对象 dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); // 2.指定定时期开始的时间和间隔的时间 // DISPATCH_TIME_NOW :第2个参数:定时器开始时间 (也可以抽出来,设置间隔时间) dispatch_time_t startTime = dispatch_time(DISPATCH_TIME_NOW, 1.0 *NSEC_PER_SEC); // intervalInSeconds :第三个参数:定时器开始后的间隔时间 // leewayInSeconds:第四个参数:间隔精准度,0代标最精准,传入一个大于0的数,代表多少秒的范围是可以接收的,主要为了提高程序性能,积攒一定的时间,Runloop执行完任务会睡觉,这个方法让他多睡一会,积攒时间,任务也就相应多了一点,而后一起执行 // 开始时间 dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 3.0 * NSEC_PER_SEC, 0 * NSEC_PER_SEC); // 3.指定定时器回调方法 dispatch_source_set_event_handler(timer, ^{ NSLog(@"___________"); }); // 开启定时器 dispatch_resume(timer);CFRunLoopSourceRef(事件源、输入源)Port-Based Sources (端口) Custom Input (自定义事件) Cocoa Perform Selector Sources按照函数的调用栈 Source0:非基于Port的 Source1:基于Port 通过内核和其他线程通信,接收分发系统事件
CFRunLoopObserverRef(观察者)能够监听RunLoop的状态改变
Paste_Image.png
CFRunLoopObserverReftypedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) { kCFRunLoopEntry = (1UL << 0), // 1 kCFRunLoopBeforeTimers = (1UL << 1), // 2 kCFRunLoopBeforeSources = (1UL << 2), // 4 kCFRunLoopBeforeWaiting = (1UL << 5), // 32 kCFRunLoopAfterWaiting = (1UL << 6), // 64 kCFRunLoopExit = (1UL << 7), // 128 kCFRunLoopAllActivities = 0x0FFFFFFFU };创建一个Observer /* 第1个参数:如何给Observer分配存储空间 第2个参数:需要监听的状态类型/kCFRunLoopAllActivities监听所有状态 第3个参数:是否每次需要监听? 第4个参数:优先级(0) 第5个参数:监听状态改变后的回调 */ CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) { switch (activity) { case kCFRunLoopEntry: NSLog(@"即将进入Runloop"); break; case kCFRunLoopBeforeTimers: NSLog(@"即将处理timer"); break; case kCFRunLoopBeforeSources: NSLog(@"即将处理Source"); break; case kCFRunLoopBeforeWaiting: NSLog(@"即将睡眠"); break; case kCFRunLoopAfterWaiting: NSLog(@"刚从休眠中唤醒"); break; case kCFRunLoopExit: NSLog(@"即将退出RunLoop"); break; default: break; } }); /* 第1个参数:要给那个RunLoop添加观察者 第2个参数:添加的Observer对象 第3个参数:在那种模式下监听 */ CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopDefaultMode); // 释放对象 CFRelease(observer);由于ARC只管理Foundation框架的内容,所以我们在Core Foundation 框架创建的对象必须手动释放。 规律: 凡是带有copy、create、retain等字眼的函数,创建出来的CF对象,都需要在最后做一次release
官方对于RunLoop的解释:Paste_Image.png
RunLoop处理逻辑,整理:自动释放池的生命周期RunLoop在进入这个 kCFRunLoopBeforeWaiting时,会对自动释放池销毁
Paste_Image.png
Runloop:在开发中有什么作用?1.NSTimer 2.ImageView的显示 3.PerformSelector 4.常驻线程 5.自动释放池
PerformSelector
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { /* - (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray<NSString *> *)modes; */ // 指定模式下进行特定的操作 [self.imageView performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@"rightPic"] afterDelay:2.0 inModes:@[NSDefaultRunLoopMode]]; }常驻线程: 默认情况下,一个线程只能使用一次,也就是只能执行一个操作 我们需要做得到就是强引用,保留线程,同时添加Source Or Timer,注:系统只会监测Source Or Timer,不会检查Observer
1.子线程的Runloop需要手动创建 2.子线程的Runloop需要手动开启 3.如果子线程的NSRunLoop没有设置Source Or Timer 那么子线程会立刻关闭。
- (void)ViewDidLoad { [super viewDidLoad]; SYThread *thread = [[SYThread alloc] initWithTarget:self selector:@selector(show) object:nil]; self.thread = thread; [thread start]; } - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { [self performSelector:@selector(test) onThread:self.thread withObject:@"时间计算" waitUntilDone:YES modes:@[NSRunLoopCommonModes]]; } - (void)show { NSLog(@"%s", __func__); // 这句话并没有意义,只是保证线程不死 [[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:NSRunLoopCommonModes]; [[NSRunLoop currentRunLoop] run]; // 上面线程不死,这句话永远不会打印 NSLog(@"-------$$$$"); } - (void)test { NSLog(@"11111"); } ---来自腾讯云社区的---雷潮
微信扫一扫打赏
支付宝扫一扫打赏