Как правильно остановить и возобновить CADisplayLink? - PullRequest
12 голосов
/ 07 декабря 2011

Я выяснил большую проблему с CADisplayLink.

У меня есть самый простой EAGLLayer с OpenGL ES 1.1, рисующий вращающийся треугольник для тестирования. Для этого необходимо вызвать метод цикла выполнения с частотой обновления экрана, поэтому я запускаю цикл запуска следующим образом:

- (void)startRunloop {
    if (!animating) {
        CADisplayLink *dl = [[UIScreen mainScreen] displayLinkWithTarget:self selector:@selector(drawFrame)];
        [dl setFrameInterval:1.0];
        [dl addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
        self.displayLink = dl;

        animating = YES;
    }
}

- (void)stopRunloop {
    if (animating) {
        [self.displayLink invalidate];
        self.displayLink = nil;
        animating = NO;
    }
}

Когда запускается тестовое приложение, я вызываю -startRunloop. Когда я нажимаю на экран, я вызываю -stopRunloop. Когда я снова нажимаю, я вызываю -startRunloop. И так далее. Пинг-понг.

Я измеряю, как часто вызывается метод -drawFrame за 20 секунд, и проверяю NSLog.

  • ПЕРВЫЙ цикл пуска / останова всегда выполняется при 100%. Я получаю максимальную частоту кадров.

  • Все последующие циклы пуска / останова показывают только около 80% производительности. Я получаю значительно меньшую частоту кадров. Но: всегда почти одинаково, +/- 2 кадра. Без тенденции к дальнейшему ухудшению даже после 50 дополнительных циклов пуска / останова.

В заключение: СОЗДАТЬ CADisplayLink, как я делал выше, хорошо, пока он не будет аннулирован или приостановлен. После этого любой новый CADisplayLink перестает работать хорошо. Даже если он создан новым и точно так же, как и раньше. То же самое верно, если я приостановлю / возобновлю его, вызвав метод -setPaused: с YES / NO.

С помощью Allocations и VM Tracker Instruments я убедился, что нет проблем с управлением памятью. Я также проверил, что -advalidate метод CADisplayLink действительно вызывается.

iOS 4.0 на устройстве (iPhone 4).

Почему это? Я что-то упустил?

Ответы [ 5 ]

8 голосов
/ 22 сентября 2015

Я думаю, что вы на самом деле хотите просто приостановить и отменить приостановку показа ссылки.

 - (void)createRunloop {
    CADisplayLink *dl = [[UIScreen mainScreen] displayLinkWithTarget:self selector:@selector(drawFrame)];
    [dl setFrameInterval:1.0];
    [dl addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    self.displayLink = dl;
    // start in running state, your choice.
    dl.paused = NO;
}
- (void)startRunloop {
    self.displayLink.paused = NO;
}
- (void)stopRunloop {
    self.displayLink.paused = YES;
}
- (void)destroyRunloop {
        [self.displayLink invalidate];
        self.displayLink = nil;
}
5 голосов
/ 16 июля 2015

Я использую

[displayLink removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];

посмотрите, работает ли он для вас

3 голосов
/ 17 декабря 2018

Для Swift 4.2

displayLink.remove(from: RunLoop.current, forMode: RunLoop.Mode.common)
1 голос
/ 01 января 2012

Вы создаете новый экземпляр отображения ссылки с каждым начальным вызовом.Вы пытались просто использовать один и тот же экземпляр?

Кроме того, вы всегда можете просто приостановить отображение ссылки.

0 голосов
/ 23 ноября 2016

Попробуйте это в Swift,

displayLink?.remove(from: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode)
...