Оптимизация получения представления

Получение часто является интенсивными действиями процессора. CPU, графическая система, сервер окна, ядро и физическая память должны все внести ресурсы, когда приложение рисует что-то на экран. Высокий расход получения делает его привлекательным кандидатом на оптимизацию. В этой главе описываются проектные решения и методы, которые можно применить в пользовательских представлениях, чтобы устранить избыточное или ненужное получение и улучшить производительность получения.

Избегите злоупотребления представлениями

NSView предлагает огромную гибкость в управлении содержанием Ваших окон и обеспечивает основной холст для рисования содержания Вашего приложения. Однако, когда Вы рассматриваете проект своих окон, думайте тщательно о том, как Вы используете представления. Несмотря на то, что представления являются удобным способом организовать содержание в окне при создании комплекса, глубоко вложенной иерархии представлений, Вы могли бы испытать проблемы производительности.

Несмотря на то, что окна Cocoa могут управлять относительно большим количеством представлений (приблизительно сто), не перенося значимых проблем производительности, это число включает и Ваши пользовательские представления и стандартные системные средства управления и подпросматривает Вас использование. Если Ваше окно имеет сотни пользовательских визуальных элементов, Вы, вероятно, не хотите реализовывать их всех как подклассы NSView. Вместо этого необходимо рассмотреть запись собственных классов, которыми может управлять более высокий уровень NSView подкласс. Код для прорисовки Вашего NSView подкласс может тогда быть оптимизирован для обработки пользовательских объектов.

Хорошим примером того, когда использовать пользовательские объекты, является браузер фотографий, выводящий на экран изображения миниатюр сотен или даже тысячи фотографий. Обертывание каждой фотографии в NSView экземпляр и был бы предельно дорогим и неэффективным. Вместо этого Вы были бы более обеспечены путем создания легкого класса для управления одной или более фотографиями и пользовательским представлением для управления тем легким классом.

Укажите непрозрачность представления

Если Вы реализуете пользовательский подкласс NSView, можно ускорить производительность получения путем объявления объекта представления как непрозрачного. Непрозрачное представление является тем, заполняющим все пиксели в довольном непрозрачные цвета использования. Система рисования Какао не должна отправлять сообщения обновления в суперпредставление для областей, покрытых одним или более непрозрачными подпредставлениями.

isOpaque метод NSView возвраты NO по умолчанию. Для объявления пользовательского объекта представления как непрозрачного переопределите этот метод и возврат YES. При создании непрозрачного представления помните, что объект представления ответственен за заполнение всех пикселей в его ограничительном прямоугольнике с помощью непрозрачных цветов. Посмотрите Непрозрачность Представления для реализации в качестве примера isOpaque.

Лишение законной силы частей представления

Какао обеспечивает два метода для перерисовки содержания Ваших представлений. Первый метод должен сразу нарисовать содержание с помощью display, displayRect:, или связанные методы. Второе должно нарисовать содержание в более позднее время путем маркировки частей представления как грязных и нуждающихся в обновлении. Этот второй метод предлагает значительно лучшую производительность и является подходящим для большинства ситуаций.

NSView определяет методы setNeedsDisplay: и setNeedsDisplayInRect: для маркировки частей Вашего представления как грязное. Какао собирает грязные прямоугольники и сохраняет их, пока вершина Вашего цикла выполнения не достигнута, в которой точке Вашему представлению говорят перерисовать себя. Прямоугольник передал в Ваш drawRect: подпрограмма является объединением грязных прямоугольников, но приложения рабочая версия 10.3 OS X и позже могут получить список отдельных прямоугольников, как описано в Ограничении Получения Улучшить Производительность.

В целом необходимо избежать вызывать display семья методов для перерисовки представлений. Если необходимо вызвать их, делайте так нечасто. Поскольку они вызывают непосредственный вызов к Вашему drawRect: подпрограмма, они могут заставить производительность замедляться значительно путем вытеснения других незаконченных операций. Они также устраняют возможность объединить другие изменения и затем перерисовать те изменения одновременно.

Ограничение рисующий для улучшения производительность

Единственный параметр drawRect: метод является прямоугольником (в частности, NSRect структура), который включает область представления, что представление просят нарисовать. Этот прямоугольник является объединением прямоугольников, отмеченных как нуждающийся обновляющий, так как экземпляр представления в последний раз получил a display сообщение. Представление может все еще нарисовать где угодно в его собственных границах, потому что Набор Приложения автоматически вырезает любое получение, выходящее за пределы прямоугольника, переданного в drawRect:. Представление может улучшить свою производительность получения, однако, путем попытки нарисовать только те части его содержания, падающие полностью или частично в отсеченном прямоугольнике.

В версии 10.3 OS X и позже, представления могут ограничить свое получение еще больше при помощи NSView методы getRectsBeingDrawn:count: и needsToDrawRect:. Эти методы обеспечивают прямой и косвенный доступ, соответственно, к подробному представлению недопустимых областей представления — т.е. его список неперекрывающихся прямоугольников — который Набор Приложения поддерживает для каждого NSView экземпляр. Набор Приложения автоматически осуществляет отсечение к этому списку прямоугольников, и можно далее улучшить производительность в представлениях, действительно объединяющих, или дорогое получение при наличии их ограничивают их получение объектами, пересекающими любой из прямоугольников в этом списке.

Представление может вызвать метод getRectsBeingDrawn:count: в drawRect: реализацию для получения списка неперекрывающихся прямоугольников, определяющих область представление, просят нарисовать. Это может тогда выполнить итерации через этот список прямоугольников, выполнив тесты на пересечение против его содержания для определения то, чему фактически нужно получение. Путем устранения тех объектов представление может избежать, чтобы ненужное получение работало и повысило эффективность получения приложения.

Перечисление 6-1 показывает основное использование getRectsBeingDrawn:count:. Это и следующий пример кода (Перечисление 6-2)), иллюстрируют методы для тестирования пересечения список прямоугольников против drawable объектов в представлении. Для перекрестного тестирования можно использовать функции, объявленные в платформе Основы NSGeometry.h заголовочный файл. NSIntersectsRect функция особенно полезна.

Перечисление 6-1  Явное перекрестное тестирование известных областей против грязных прямоугольников

 (void) drawRect:(NSRect)aRect {
    const NSRect *rects;
    int count, i;
    id thing;
    NSEnumerator *thingEnumerator = [[self arrayOfAllThingsIDraw] objectEnumerator];
    [self getRectsBeingDrawn:&rects count:&count];
    while (thing = [thingEnumerator nextObject]) {
        // First test against coalesced rect.
        if (NSIntersectsRect([thing bounds], aRect)) {
        // Then test per dirty rect
            for (i = 0; i < count; i++) {
                if (NSIntersectsRect([thing bounds], rects[i])) {
                    [self drawThing:thing];
                    break;
                }
            }
        }
    }
}

Для каждого объекта, который представление может потенциально нарисовать, это drawRect: реализация сначала тестирует ограничительный прямоугольник объекта против drawRect: (настороженный) параметр метода. Если эти два пересекаются, представление тогда определяет, пересекают ли границы объекта какой-либо из прямоугольников в списке, полученном getRectsBeingDrawn:count:. Если это действительно пересекается, представление рисует объект (или просит, чтобы он нарисовал себя).

Поскольку представлению свойственно представить свое содержание путем рисования ряда индивидуально расположенных элементов, NSView класс обеспечивает удобный метод, по существу выполняющий большую часть работы в Перечислении 6-1 для Вас. Этот метод, needsToDrawRect:, не требует, чтобы Вы выбрали список грязных прямоугольников с getRectsBeingDrawn:count: или выполните внутренний цикл для перекрестного тестирования. Получающийся код, как проиллюстрировано в Перечислении 6-2, намного более чист и более прост.

Перечисление 6-2  Упрощенное перекрестное использование тестирования needsToDrawRect:

- (void) drawRect:(NSRect)aRect {
    id thing;
    NSEnumerator *thingEnumerator = [[self arrayOfAllThingsIDraw] objectEnumerator];
    while (thing = [thingEnumerator nextObject]) {
        if ([self needsToDrawRect:[thing bounds]]) {
            [self drawThing:thing];
        }
    }
}

needsToDrawRect: метод оптимизирован для эффективного отклонения объектов, лежащих полностью вне границ области, нарисованной путем использования того же “тривиального отклонения” тест как используемый в Перечислении 6-1.

Подавление отсечения по умолчанию

По умолчанию Какао автоматически отсекает получение, сделанное в a drawRect: метод к области, которую представление просят нарисовать. Если представление рисует в области, не находящейся в пределах отсеченных границ, ничего подобного, что получение находит своим путем к экрану. Для большинства видов представлений это - надлежащее поведение, поскольку оно предотвращает получение в областях окна, принадлежавших другим представлениям, и делает так, не требуя, чтобы представление придирчиво ограничило его получение. Но при некоторых обстоятельствах, это не могло бы быть тем, что Вы хотите. Отсечение подвергается установке, осуществлению и затратам очистки, которых Вы могли бы хотеть избежать, если Вы можете.

В этих ситуациях Ваше пользовательское представление может переопределить NSView метод wantsDefaultClipping и возвратитесь NO:

- (BOOL)wantsDefaultClipping {
    return NO;
}

Очевидно, отсутствие вынужденного отсечения представляет опасности, а также возможности. Вы не должны рисовать вне списка прямоугольников, возвращенных getRectsBeingDrawn:count: поскольку это могло повредить получение в других представлениях.

Можно проявить один из двух (ответственных) подходов:

Одна возможная стратегия реализации drawRect: в этом случае должен выполнить итерации по списку нарисованных прямоугольников. Клип каждому и рисует содержание, один прямоугольник за один раз. Улучшается ли такая стратегия или уменьшается, производительность получения в Вашем представлении зависит много от довольного представления и типичное поведение получения.

Рисование во время живого изменения размеров окна

Живое изменение размеров окна является областью, где плохо оптимизированный код для прорисовки становится особенно очевидным. Когда пользователь изменяет размеры Вашего окна, перемещение окна должно быть гладким. Если Ваш код пытается выполнить слишком много работы в это время, перемещение окна может казаться изменчивым и безразличным пользователю.

Следующие разделы представляют Вас нескольким опциям для улучшения Вашего живого кода изменения размеров. В зависимости от которых версий OS X Вы предназначаетесь, Вы могли бы использовать один или больше этих опций в Вашей реализации.

Нарисуйте минимально

То, когда живое изменяет размеры работы, происходит, скорость обязательна. Самый простой способ улучшить скорость состоит в том, чтобы выполнить меньше работы. Поскольку качество обычно менее важно во время живого, изменяют размеры работы, можно взять некоторые ярлыки для ускорения получения. Например, если Ваш код для прорисовки обычно выполняет вычисления высокой точности для определения расположения элементов, Вы могли бы заменить те вычисления быстрыми приближениями во время живого, изменяют размеры работы.

NSView обеспечивает inLiveResize метод для сообщения, когда живое изменяют размеры работы, имеет место. Можно использовать этот метод в Вашем drawRect: подпрограмма, чтобы сделать условное получение, как показано в следующем примере:

- (void) drawRect:(NSRect)rect
{
    if ([self inLiveResize])
    {
        // Draw a quick approximation
    }
    else
    {
        // Draw with full detail
    }
}

Другой способ минимизировать работу состоит в том, чтобы перерисовать только те области Вашего представления, представленные во время изменять размеры работы. При предназначении для приложения для версии 10.3 OS X можно использовать getRectsBeingDrawn:count: метод для получения представленных прямоугольников. Если Вы предназначаетесь для версии 10.4 OS X или позже, getRectsExposedDuringLiveResize:count: метод предоставлен для возврата только прямоугольников, представленных путем изменения размеров.

Живое какао изменяет размеры уведомлений

Можно использовать viewWillStartLiveResize и viewDidEndLiveResize методы NSView чтобы помочь оптимизировать Ваше живое изменяют размеры кода. Какао сразу вызывает эти методы прежде и сразу после того, как живое изменяет размеры работы, имеет место. Можно использовать viewWillStartLiveResize метод к данным кэша или делает любую другую инициализацию, которая может помочь скорости встать, Ваши живые изменяют размеры кода. Вы используете viewDidEndLiveResize метод, чтобы очистить Ваши кэши и возвратить Ваше представление его нормальному состоянию.

Вызовы какао viewWillStartLiveResize и viewDidEndLiveResize для каждого представления в иерархии Вашего окна. Это сообщение отправляется только один раз в каждое представление. Представления, добавленные в течение середины живого, изменяют размеры работы, не получают сообщение. Точно так же при удалении представлений, прежде чем концы работы изменения размеров, те представления не получат viewDidEndLiveResize сообщение.

При использовании этих методов для создания приближения с низкой разрешающей способностью содержания, Вы могли бы хотеть лишить законной силы содержание своего представления в Вашем viewDidEndLiveResize метод. Лишение законной силы представления вызывает, это быть перерисованным в полном разрешении за пределами живого изменяет размеры цикла.

Если Вы переопределяете также viewWillStartLiveResize или viewDidEndLiveResize, удостоверьтесь, что отправили сообщение в super позволить подпредставлениям подготавливаться к изменять размеры работе также. Если необходимо добавить представления, прежде чем изменять размеры работа начнется, удостоверьтесь, что сделали так перед вызовом super если Вы хотите, чтобы то представление получило viewWillStartLiveResize сообщение.

Содержание окна заповедника

В OS X v10.4 и позже, Какао предлагает Вам, способ быть еще более умным об обновлении Вашего содержания во время живого изменяет размеры работы. Оба NSWindow и NSView включайте поддержку сохранения содержания во время работы. Этот метод позволяет Вам решить, какое содержание действительно недопустимо и должно быть перерисовано.

Для поддержки сохранения содержания необходимо сделать следующее:

  1. Переопределите preservesContentDuringLiveResize метод в Вашем пользовательском представлении. Ваша реализация должна возвратиться YES указать, что представление поддерживает сохранение содержания.

  2. Переопределите свое представление setFrameSize: метод. Ваша реализация должна лишить законной силы любые части Вашего представления, которое должно быть перерисовано. Как правило, это включает только прямоугольные области, представленные, когда увеличился размер представления.

Найти области Вашего представления, представленные во время изменения размеров, NSView обеспечивает два метода. rectPreservedDuringLiveResize метод возвращает прямоугольную область Вашего не изменявшегося представления. getRectsExposedDuringLiveResize:count: метод возвращает список прямоугольников, представляющих любые недавно представленные области. Для большинства представлений Вы должны только передать прямоугольники, возвращенные этим вторым методом к setNeedsDisplayInRect:. Первый метод предоставлен в случае, если все еще необходимо лишить законной силы остальную часть представления.

Следующий пример обеспечивает реализацию по умолчанию, которую можно использовать для Вашего setFrameSize: метод. В примере ниже, проверки реализации, чтобы видеть, изменяется ли представление. Если это, и если какие-либо прямоугольники были представлены работой изменения размеров, это получает недавно представленные прямоугольники и лишает законной силы их. Если уменьшенный размер представления, этот метод ничего не делает.

- (void) setFrameSize:(NSSize)newSize
{
    [super setFrameSize:newSize];
 
    // A change in size has required the view to be invalidated.
    if ([self inLiveResize])
    {
        NSRect rects[4];
        int count;
        [self getRectsExposedDuringLiveResize:rects count:&count];
        while (count-- > 0)
        {
            [self setNeedsDisplayInRect:rects[count]];
        }
    }
    else
    {
        [self setNeedsDisplay:YES];
    }
}