Меня интересует цикл NSRunLoop, особенно для основного цикла выполнения. Через CFRunLoopObserverRef
мы можем узнать о нем больше:
CFRunLoopObserverRef observerRef = CFRunLoopObserverCreateWithHandler(NULL, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
if (activity == kCFRunLoopBeforeTimers) {
weakSelf.runloopId += 1;
}
NSLog(@"RunloopId : %lu, Activity : %ld, %lf\n", (unsigned long)weakSelf.runloopId, activity, [[NSDate date] timeIntervalSince1970]);
});
CFRunLoopAddObserver(CFRunLoopGetMain(), observerRef, kCFRunLoopCommonModes);
Мы можем игнорировать kCFRunLoopEntry
и kCFRunLoopExit
для основного цикла выполнения и сосредоточиться на kCFRunLoopBeforeTimers
, kCFRunLoopBeforeSources
, kCFRunLoopBeforeWaiting
и kCFRunLoopAfterWaiting
.
Согласно Последовательность событий цикла выполнения и CFRunLoop.c,
- один цикл основного цикла выполнения начинается с
kCFRunLoopBeforeTimers
, - затем
kCFRunLoopBeforeSources
, - после двух вышеупомянутых событий, если ничего не ожидается, иди спать,
поэтому, на мой взгляд, когда основной цикл переходит в спящий режим, он должен простаивать.
Обычное состояние ожидания
Используя мой код выше, когда я открываю приложение (в симуляторе) и просто оставляю его там, чтобы оно не работало, обратные вызовы событий цикла выполнения будут выводить журнал на консоль:
2016-03-08 21:58:33.522 CaptureJitterDemo[25272:1312010] RunloopId : 156, Activity : 2, 1457445513.522846
2016-03-08 21:58:33.523 CaptureJitterDemo[25272:1312010] RunloopId : 156, Activity : 4, 1457445513.522985
2016-03-08 21:58:33.523 CaptureJitterDemo[25272:1312010] RunloopId : 156, Activity : 32, 1457445513.523144
2016-03-08 21:58:49.002 CaptureJitterDemo[25272:1312010] RunloopId : 156, Activity : 64, 1457445529.002502
2016-03-08 21:58:49.002 CaptureJitterDemo[25272:1312010] RunloopId : 157, Activity : 2, 1457445529.002820
2016-03-08 21:58:49.003 CaptureJitterDemo[25272:1312010] RunloopId : 157, Activity : 4, 1457445529.003044
Activity : 32
означает beforeWaiting
, а Activity : 64
означает afterWaiting
, и между двумя состояниями основной стек вызовов выглядит следующим образом:
Делать нечего, только ждать.
Удивительное состояние ожидания
Иногда я обнаруживаю, что что-то может произойти между beforeWaiting
и afterWaiting
:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(@"begin of url request... %lf\n", [[NSDate date] timeIntervalSince1970]);
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"https://c2.staticflickr.com/4/3771/9914251535_366536a515_h.jpg"]];
if (data) {
NSLog(@"end of url request : %lf\n", [[NSDate date] timeIntervalSince1970]);
}
}
когда я выбираю одну строку, консоль печатает:
2016-03-08 23:00:07.927 CaptureJitterDemo[25855:1334505] RunloopId : 27, Activity : 2, 1457449207.927184
2016-03-08 23:00:07.927 CaptureJitterDemo[25855:1334505] RunloopId : 27, Activity : 4, 1457449207.927399
2016-03-08 23:00:07.927 CaptureJitterDemo[25855:1334505] RunloopId : 27, Activity : 32, 1457449207.927624
2016-03-08 23:00:07.929 CaptureJitterDemo[25855:1334505] begin of url request... 1457449207.929021
2016-03-08 23:00:10.277 CaptureJitterDemo[25855:1334505] end of url request : 1457449210.276982
2016-03-08 23:00:10.278 CaptureJitterDemo[25855:1334505] RunloopId : 27, Activity : 64, 1457449210.278046
2016-03-08 23:00:10.278 CaptureJitterDemo[25855:1334505] RunloopId : 28, Activity : 2, 1457449210.278244
2016-03-08 23:00:10.278 CaptureJitterDemo[25855:1334505] RunloopId : 28, Activity : 4, 1457449210.278486
2016-03-08 23:00:10.278 CaptureJitterDemo[25855:1334505] RunloopId : 28, Activity : 32, 1457449210.278752
Согласно журналам, между beforeWaiting
и afterWaiting
приложение выполнило одну задачу запроса URL. Это меня смущает.
Мое непонимание сначала
Сначала я подумал, что синхронный запрос URL в основном потоке приведет к тому, что основной цикл выполнения перейдет в состояние waiting
. Но позже я обнаружил, что приложение сначала перешло в состояние waiting
, а затем произошел запрос URL.
Итак, теперь это явление, когда основной цикл выполнения переходит в состояние waiting
, он все еще может выполнять синхронный запрос URL-адреса, не просыпаясь — шутите?
Если я перемещу код на viewDidAppear
:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSLog(@"begin of url request... %lf\n", [[NSDate date] timeIntervalSince1970]);
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"https://c2.staticflickr.com/4/3771/9914251535_366536a515_h.jpg"]];
if (data) {
NSLog(@"end of url request : %lf\n", [[NSDate date] timeIntervalSince1970]);
}
}
консоль печатает:
2016-03-08 23:42:25.616 CaptureJitterDemo[26268:1377354] RunloopId : 0, Activity : 1, 1457451745.616474
2016-03-08 23:42:25.617 CaptureJitterDemo[26268:1377354] RunloopId : 1, Activity : 2, 1457451745.617201
2016-03-08 23:42:25.617 CaptureJitterDemo[26268:1377354] RunloopId : 1, Activity : 4, 1457451745.617346
2016-03-08 23:42:25.618 CaptureJitterDemo[26268:1377354] begin of url request... 1457451745.618176
2016-03-08 23:42:27.832 CaptureJitterDemo[26268:1377354] end of url request : 1457451747.832428
2016-03-08 23:42:27.832 CaptureJitterDemo[26268:1377354] RunloopId : 2, Activity : 2, 1457451747.832818
2016-03-08 23:42:27.832 CaptureJitterDemo[26268:1377354] RunloopId : 2, Activity : 4,
что кажется более приемлемым.
Что я действительно хочу сделать
Я хочу рассчитать время, затрачиваемое на цикл, в основном цикле выполнения, но время простоя не должно быть включено.
Цикл означает:
- Если сон не случился:
kCFRunLoopBeforeTimers
->kCFRunLoopBeforeSources
->kCFRunLoopBeforeTimers
; - Если случился сон:
kCFRunLoopBeforeTimers
->kCFRunLoopBeforeSources
->kCFRunLoopBeforeWaiting
->kCFRunLoopAfterWaiting
->kCFRunLoopBeforeTimers
;
Для стоимости времени с kCFRunLoopBeforeWaiting
по kCFRunLoopAfterWaiting
:
- если это нормальное состояние ожидания, время следует исключить, потому что оно действительно простаивает;
- в противном случае, если это неожиданное состояние ожидания, время должно быть включено, потому что у него есть дела;
Спасибо за любую помощь :)