iOS_Run_Loop事件监听

Run Loop监听事件列表

  1. run loop的入口事件。
  2. run loop即将处理计时器事件。
  3. run loop即将处理输入源事件。
  4. run loop即将休眠。
  5. run loop已经被唤醒,但是还没有处理唤醒它的事件。
  6. run loop 退出。

监听代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
- (void)threadMain
{
NSRunLoop* myRunLoop = [NSRunLoop currentRunLoop];
CFRunLoopObserverContext context = {0,
(__bridge void *)(self), NULL, NULL, NULL};
CFRunLoopObserverRef observer = CFRunLoopObserverCreate(kCFAllocatorDefault,
kCFRunLoopAllActivities, YES, 0, &myRunLoopObserver, &context);
if (observer)
{
CFRunLoopRef cfLoop = [myRunLoop getCFRunLoop];
CFRunLoopAddObserver(cfLoop, observer, kCFRunLoopDefaultMode);
}
[NSTimer scheduledTimerWithTimeInterval:0.1 target:self
selector:@selector(doFireTimer:) userInfo:nil repeats:YES];
NSInteger loopCount = 10;
do
{
[myRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
// 1s 后才执行 --操作
loopCount--;
}
while (loopCount);
}
static void myRunLoopObserver(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info)
{
switch (activity) {
case kCFRunLoopEntry:
NSLog(@"kCFRunLoopEntry");
break;
case kCFRunLoopBeforeTimers:
NSLog(@"kCFRunLoopBeforeTimers");
break;
case kCFRunLoopBeforeSources:
NSLog(@"kCFRunLoopBeforeSources");
break;
case kCFRunLoopBeforeWaiting:
NSLog(@"kCFRunLoopBeforeWaiting");
break;
case kCFRunLoopAfterWaiting:
NSLog(@"kCFRunLoopAfterWaiting");
break;
case kCFRunLoopExit:
NSLog(@"kCFRunLoopExit");
break;
default:
break;
}
}
- (void)doFireTimer:(NSTimer *)timer {
NSLog(@"timer action");
}

解释:threadMain方法是apple官方文档中的示例代码,接下来主要讲解里面的几个主要函数。

CFRunLoopActivity 监听事件

1
2
3
4
5
6
7
8
9
typedef enum CFRunLoopActivity : CFOptionFlags {
kCFRunLoopEntry = (1UL << 0),
kCFRunLoopBeforeTimers = (1UL << 1),
kCFRunLoopBeforeSources = (1UL << 2),
kCFRunLoopBeforeWaiting = (1UL << 5),
kCFRunLoopAfterWaiting = (1UL << 6),
kCFRunLoopExit = (1UL << 7),
kCFRunLoopAllActivities = 0x0FFFFFFFU
} CFRunLoopActivity;

CFRunLoopObserverContext是一个结构体

1
2
3
4
5
6
7
typedef struct {
CFIndex version;
void * info;
const void *(*retain)(const void *info);
void (*release)(const void *info);
CFStringRef (*copyDescription)(const void *info);
} CFRunLoopObserverContext;

version: 结构体的版本号,必须是0

retain: 对info进行retaion操作的回调方法。

release: 对info进行release操作的回调方法。

copyDescription: 对info进行copyDescription操作的回调方法。

info: 由程序定义的一个指针,在创建的时候与run loop的监听者相关联。这个指针在context内部的所有回调函数中传递。

CFRunLoopObserverCreate 创建run loop监听者对象。

1
2
3
4
5
6
CFRunLoopObserverRef CFRunLoopObserverCreate(CFAllocatorRef allocator,
CFOptionFlags activities,
Boolean repeats,
CFIndex order,
CFRunLoopObserverCallBack callout,
CFRunLoopObserverContext *context);

allocator: 内存分配器,为新的对象分配内存,NULL或kCFAllocatorDefault表示默认的内存分配器。

activities: 要监听那些事件。参考:CFRunLoopActivity。

repeats: 是否重复监听。

callout: 回调函数。

context: 上下文,参考:CFRunLoopObserverContext

runUntilDate 执行run loop直到一个指定时间。在这段时间内处理所有依附于它的输入源的数据。如果没有输入源或者时间源依附于run loop, 这个方法将立即退出,否则的话,他将以NSDefaultRunLoopMode 为参数不停的调用runMode: beforeDate:一直到指定的时间。

手动的从run loop中移除所有的输入源和timer不能确保run loop会终止。macOS 可以根据需要装载和移除额外的输入源,以处理针对接收者线程的请求,这些源可以阻止运行循环退出。

监听主线程Run Loop事件

对于主线程而言,run loop 随着应用程序的启动而启动,我们无法控制其启动和退出。我们可以对其运行状态监听。

didFinishLaunchingWithOptions方法中调用threadMain,会发现控制台在不断的打印数据。

需要注意的是:对于主线程而言threadMain方法中while不是必须的,因为程序运行期间,主线程的run loop不会终止。

监听后台线程Run Loop事件

didFinishLaunchingWithOptions方法中调用

1
[self performSelectorInBackground:@selector(threadMain) withObject:nil];

会发现控制台会打印100次 timer action,这100次来自于10次循环*每秒10调用timer。

小结

这篇文章主要讲解了关于Run Loop的事件监听,Run Loop是iOS开发和面试中经常出现的知识点,我也在进一步的学习,如果有哪些不对的地方,还希望您的指正。