ios - Por qué una implementación vacía de drawRect: afectará negativamente el rendimiento durante la animación
objective-c cocoa-touch (2)
Para saber cuándo usar -drawRect: y cuándo hacer cosas como usar un UIImageView, tendré que explicar un poco más:
UIView y CGLayer esencialmente se ocupan de imágenes fijas. Estas imágenes se cargan en la tarjeta gráfica (si conoce OpenGL, piense en una imagen como una textura y en UIView / CGLayer como un polígono que muestre dicha textura). Una vez que una imagen está en la GPU, se puede dibujar muy rápidamente, e incluso varias veces, y (con una leve penalización de rendimiento) incluso con niveles variables de transparencia alfa encima de otras imágenes.
CoreGraphics / Quartz es una API para generar imágenes. Toma un búfer de píxeles (de nuevo, piensa en la textura OpenGL) y cambia los píxeles individuales dentro de él. Todo esto sucede en la memoria RAM y en la CPU, y solo una vez que Quartz finaliza, la imagen se "vacia" de nuevo a la GPU. Este viaje de ida y vuelta para obtener una imagen de la GPU, cambiarla y luego cargar toda la imagen (o al menos una parte comparativamente grande) de vuelta a la GPU es bastante lenta. Además, el dibujo real que hace Quartz, aunque realmente rápido para lo que estás haciendo, es mucho más lento que lo que hace la GPU.
Eso es obvio, teniendo en cuenta que la GPU se mueve principalmente en píxeles sin cambiar en grandes fragmentos. Quartz tiene acceso aleatorio de píxeles y comparte la CPU con redes, audio, etc. Además, si tiene varios elementos que dibuja usando Quartz al mismo tiempo, debe volver a dibujarlos cuando cambie uno, luego cargue el todo, mientras que si cambias una imagen y luego dejas que UIViews o CGLayers peguen en tus otras imágenes, puedes salirte con la carga de cantidades mucho menores de datos a la GPU.
Cuando no implementa -drawRect :, la mayoría de las vistas solo pueden optimizarse. No contienen ningún píxel, por lo que no pueden dibujar nada. Otras vistas, como UIImageView, solo dibujan un UIImage (que, una vez más, es esencialmente una referencia a una textura, que probablemente ya se haya cargado en la GPU). Entonces, si dibujas el mismo UIImage 5 veces con UIImageView, solo se carga en la GPU una vez y luego se dibuja en la pantalla en 5 ubicaciones diferentes, lo que nos ahorra tiempo y CPU.
Cuando implementa -drawRect :, esto hace que se cree una nueva imagen. Luego recurres a eso en la CPU usando Quartz. Si dibuja un UIImage en su drawRect, es probable que descargue la imagen de la GPU, la copie en la imagen que está dibujando y, una vez que haya terminado, carga esta segunda copia de la imagen a la tarjeta gráfica. Por lo tanto, está usando dos veces la memoria de la GPU en el dispositivo.
Por lo tanto, la manera más rápida de dibujar es generalmente mantener el contenido estático separado del contenido variable (en subclases UIViews / UIView / CGLayers por separado). Cargue contenido estático como un UIImage y dibuje usando UIImageView y coloque contenido generado dinámicamente en tiempo de ejecución en drawRect. Si tiene contenido que se dibuja repetidamente, pero que por sí solo no cambia (es decir, 3 íconos que se muestran en la misma ranura para indicar algún estado) también use UIImageView.
Una advertencia: existe demasiada UIViews. Las áreas particularmente transparentes tienen un costo mayor en la GPU para dibujar, ya que deben mezclarse con otros píxeles detrás de ellas cuando se muestran. Esta es la razón por la que puede marcar un UIView como "opaco", para indicarle a la GPU que puede borrar todo lo que está detrás de esa imagen.
Si tiene contenido que se genera dinámicamente en el tiempo de ejecución, pero permanece igual durante la duración de la aplicación (por ejemplo, una etiqueta que contiene el nombre de usuario), puede tener sentido dibujar todo una vez usando Quartz, con el texto, el borde de la etiqueta, etc., como parte del fondo. Pero eso generalmente es una optimización que no es necesaria a menos que la aplicación Instruments lo indique de manera diferente.
Estoy subclasificando mi clase UIView
. El código autogenerado de Xcode (estoy usando 4.6.3) dice:
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
Me planteó algunas preguntas en mi mente:
1) Por qué una implementación vacía de drawRect:
provocará un rendimiento adverso durante la animación.
2) ¿Cuándo debería implementar drawRect:
3) Si estoy implementando drawRect:
entonces, ¿qué se debe tomar como precaución para la mejor práctica?
Only override draw() if you perform custom drawing.
Esto significa que si no realizamos un dibujo personalizado, no deberíamos escribir la función de anulación. Se usa el draw()
"predeterminado" draw()
. Todos los gráficos, si los hay, se manejan "de forma nativa" para optimizar el rendimiento.
An empty implementation adversely affects performance during animation.
Si override draw()
, pero no escribimos ningún código en la función dejando este func
vacío, eso es (1) una implementación de override draw()
y también es (2) una vacía también. Esto degradará el rendimiento. El siguiente código
public override func (_ r: CGRect) {
// empty
}
no es eficiente durante la animación ¿Es posible que el sistema intente redibujar todo el lienzo en lugar de volver a dibujar solo la parte afectada?