В частности, вы должны анализировать с этой точки зрения распределение памяти для следующих объектов.
■ Объекты Bitmap. Часто возникают ситуации, в которых в качестве временного хранилища для изображения или части изображения, создаваемых вашим приложением, удобно использовать объект Bitmap. Если вашему приложению требуется пространство для рисования, то в этом нет ничего необычного; единственное, чего следует избегать — так это создания и уничтожения многих битовых изображений в циклах рисования. Обычно гораздо лучше иметь одно рабочее пространство, многократно используемое в качестве общего ресурса, чем нести дополнительные затраты, связанные с непрерывным созданием и уничтожением временных изображений. Размер такой временной памяти должен быть равен размеру наибольшего временного изображения, которое может понадобиться вашим процедурам (но не более того). Если временную память необходимо очищать, прежде чем использовать ее для рисования, то это можно сделать легко и быстро при помощи вызова Graphics.Clear(). Кроме того, как продемонстрировано в приведенном выше примере кода, часто используемые растровые изображения имеет смысл загружать и кэшировать в памяти. Следите за тем, чтобы в каждый момент времени загружался только один экземпляр растрового изображения. Загрузка нескольких экземпляров идентичных изображений приведет к напрасному расходованию больших объемов памяти.
■ Объекты Graphics. Если требуется постоянно перерисовывать растровое изображение, как это, например, приходится делать при перерисовке кадров игрового поля, то, вероятно, наряду с целевым экранным изображением целесообразно кэшировать также объекты Graphics внеэкранных изображений. Поступая таким образом, вы избавитесь от необходимости непрерывного размещения этих объектов в памяти и их удаления. Не распределяйте в циклах рисования память для объектов Graphics растровых изображений или форм более одного раза; в идеальном случае следует вообще избегать размещения и удаления таких объектов внутри циклов. To же самое касается и любых других битовых карт, в которых вы должны выполнять рисование; кэшируйте их объекты Graphics, а не создавайте их каждый раз заново. Учтите, что объекты Graphics нужны только для тех битовых карт, которые вы используете для создания изображений; если битовая карта используется лишь в качестве источника информации об изображении, то объект Graphics для нее вам вообще не нужен.
■ Объекты Font, Brush, Pen, ImageAttribute. Распространенной ошибкой разработчиков, причины которой вполне объяснимы, является их чрезмерное увлечение управлением временем жизни ресурсов на микроуровне, не сопровождающееся глубоким анализом их использования на макроуровне. В процессе создания графического изображения вам может потребоваться пройти, скажем, через 30 отдельных этапов, в ходе которых рисуются линии, закрашиваются эллипсы, создается текст и копируются битовые образы. Выполнение каждой из этих операций требует использования некоторой комбинации перьев, кистей, шрифтов, то есть объектов Pen, Brush, Font, а если в дело включаются еще и маски прозрачности, то и объектов ImageAttribute. В случае мобильных устройств ситуация усугубляется тем, что в .NET Compact Framework, в отличие от .NET Framework, статические версии базовых кистей и перьев не предусмотрены. Так, в версии NET Framework, ориентированной на настольные компьютеры, существуют, например, объекты System.Drawing.Pens.Blue и System.Drawing.Brushes.DarkOrange, но в NET Compact Framework эти объекты приходится распределять. Решение этой проблемы заключается в создании собственного глобального набора объектов Pen и Brush, который вы будете использовать для нужд рисования во всем приложении. Вы должны тщательно просмотреть все циклы рисования в приложении, обращая особое внимание на случаи повторного создания идентичных ресурсов и избавляясь от подобной избыточности в коде. Если в вашем приложении непрерывно выполняются операции рисования, то такие объекты, как шрифты, перья, кисти и маски прозрачности, должны либо однажды распределяться в цикле рисования, либо глобально кэшироваться.
■ Типы, которые преобразуются (или, как еще говорят, "'упаковываются") в объекты. Наиболее распространенным типом значений, используемым в графических кодах, является структура System.Drawing.Rectangle. Обычно работа с типами, соответствующими значениям, отличается высокой эффективностью, поскольку их можно размещать в стеке (а не в глобальном пуле свободной памяти, или куче, как объекты). В то же время, значения также могут рассматриваться как объекты и размещаться в массивах или коллекциях или передаваться всюду, где допускается использование объектного типа. Тщательно следите за тем, чтобы не происходило неявное постоянное распределение и освобождение памяти, обусловленное "упаковкой" значений в объекты и их обратной "распаковкой".
Заботясь о том, чтобы в графическом коде приложения не выполнялось непрерывное создание и уничтожение часто используемых объектов, вы не должны забывать о необходимости следить за использованием глобальной памяти своего мобильного приложения. Если в графических функциональных возможностях нуждаются лишь отдельные части приложения, то следует подумать об освобождении занимаемых ими ресурсов, как только приложение входит в состояние, в котором в течение некоторого времени графические ресурсы ему не будут нужны. Хорошей практикой проектирования считается использование конечного автомата и определение тех графических ресурсов, от которых при выходе приложения из определенных состояний можно освободиться.
Резюме
От того, что вы напишете код, обеспечивающий отличную реакцию пользовательского интерфейса, алгоритмы вашего приложения работать быстрее не станут. Это никоим образом не ускорит победный марш разработки приложения "до полного завершения". Более того, код приложения может даже усложниться. Единственная причина, по которой следует заботиться о высокой реактивности интерфейса и стремиться к высокой производительности его кода, заключается в том, чтобы создать для пользователя более комфортные условия, но разве ради этого не стоит потрудиться? Если вы хотите, чтобы пользователь поставил самую высокую оценку вашему приложению, вы не должны жалеть времени на доводку и отшлифовку таких вещей, как способность интерфейса к отклику. Как и в случае других аспектов производительности, выполнение этой работы нельзя откладывать до завершающих этапов процесса разработки. В конце разработки вы еще смогли бы добавить что-то наподобие курсоров ожидания в тех точках, где приложение может тормозиться, но это все равно, что красиво покрасить фасад плохо построенного дома — это лучше, чем вообще ничего, но, в сущности, не так уж и много. Никогда не забывайте о необходимости обеспечения высокой интерактивности пользовательского интерфейса и всегда в явном виде включайте это требование в качестве критерия прохождения каждой контрольной точки разработки. Лучше всего решать подобные проблемы сразу же после их выявления, пока они не успели накрепко зацементироваться в структуру приложения.
В основе высокой производительности пользовательского интерфейса лежит активное управление условиями взаимодействия с ним конечного пользователя. Ваше мобильное приложение должно обеспечивать такие условия, при которых пользователь в процессе навигации в пределах приложения чувствует его ответную реакцию и не испытывает раздражения. Пользовательский интерфейс инициирует сложнейшую систему взаимодействий между кодом приложения и библиотеками времени выполнения, поверх которых приложение построено. Этот процесс сопровождается созданием элементов управления, используемых для заполнения форм, генерацией событий, а также многочисленными видами взаимодействия различного уровня между кодом и средой выполнения. Высокая производительность обеспечивается использованием оптимизированных в этом отношении средств среды выполнения, структурированием кода пользовательского интерфейса таким образом, чтобы пользователь не чувствовал себя изолированным от вычислительного процесса при выполнении задач, требующих длительного времени, а также целенаправленной количественной оценкой эффективности кода пользовательского интерфейса и включения в него средств контроля хода выполнения, которые позволят вам разобраться в том, что именно происходит за кулисами пользовательского интерфейса.