2009-08-18 6 views
0

Ceci est un exemple assez simple et ne serait probablement pas faire beaucoup de différence de toute façon, mais dire que j'ai ce code de dessin en vue de tracer un gradient:Est-il nécessaire de mettre en cache les opérations courantes pour -drawRect: sur l'iPhone?

@interface SomeView : UIView 
@end 

@implementation SomeView 

- (void)drawRect:(CGRect)rect 
{ 
    const CGContextRef ctx = UIGraphicsGetCurrentContext(); 

    // Set fill color to white 
    CGContextSetGrayFillColor(ctx, 1.0f, 1.0f); 
    CGContextFillRect(ctx, rect); 

    // Create a fancy, albeit ugly, orange gradient 
    const CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB(); 
    const CGFloat components[] = { 1.0, 0.5, 0.4, 1.0, // Start color 
            0.8, 0.8, 0.3, 1.0 }; // End color 
    CGGradientRef gloss; 
    gloss = CGGradientCreateWithColorComponents(rgbColorSpace, components, NULL, 2); 
    CGColorSpaceRelease(rgbColorSpace); 

    // Draw the gradient 
    const CGPoint endPoint = {rect.origin.x, 
           rect.origin.y + floor(rect.size.height/2.0f)}; 
    CGContextDrawLinearGradient(ctx, gloss, rect.origin, endPoint, 0); 
    CGGradientRelease(gloss); 
} 

@end 

Je sais que cela est un exemple très négligeable , mais vous pouvez imaginer l'inquiétude si j'avais des valeurs plus complexes à réutiliser. Est-il nécessaire de les mettre en cache, ou est-ce que Cocoa-Touch le fait pour vous avec CALayers?

Voici un exemple de ce que je veux dire par la mise en cache:

@interface SomeView : UIView 
{ 
    CGGradientRef gloss; 
} 
@end 

@implementation SomeView 

- (id)initWithFrame:(CGRect)frame 
{ 
    if (self = [super initWithFrame:frame]) { 
     // Create a fancy, albeit ugly, orange gradient only once here instead 
     const CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB(); 
     const CGFloat components[] = { 1.0, 0.5, 0.4, 1.0, // Start color 
             0.8, 0.8, 0.3, 1.0 }; // End color 
     CGGradientRef gloss; 
     gloss = CGGradientCreateWithColorComponents(rgbColorSpace, components, NULL, 2); 
     CGColorSpaceRelease(rgbColorSpace); 
    } 
    return self; 
} 

- (void)dealloc 
{ 
    CGGradientRelease(gradient); 
    [super dealloc]; 
} 

- (void)drawRect:(CGRect) 
{ 
    const CGContextRef ctx = UIGraphicsGetCurrentContext(); 

    // Set fill color to white 
    CGContextSetGrayFillColor(ctx, 1.0f, 1.0f); 
    CGContextFillRect(ctx, rect); 

    // Draw the gradient 
    const CGPoint endPoint = {rect.origin.x, 
           rect.origin.y + floor(rect.size.height/2.0f)}; 
    CGContextDrawLinearGradient(ctx, gloss, rect.origin, endPoint, 0); 
} 

@end 

Vous pouvez évidemment voir le compromis entre ici; Surtout si j'avais beaucoup de ces vues, cela pourrait prendre plus de mémoire avec cette technique par rapport aux performances de dessin éventuellement pire dans le premier. Cependant, je ne suis même pas sûr si est beaucoup d'un compromis parce que je ne sais pas quelle magie Cocoa fait dans les coulisses. Quelqu'un pourrait-il expliquer?

Répondre

1

La seule chose "mise en cache" est le résultat du message drawRect:. Il est mis en cache jusqu'à son invalidation, auquel cas le message est appelé à nouveau. Cocoa et Cocoa-Touch ne mettront pas en cache les objets que vous utilisez dans vos méthodes.

Vous pouvez les mettre en cache vous-même, comme vous l'avez fait dans votre deuxième exemple. Cependant, je suggère de tester de telles optimisations en utilisant un profileur tel que Instruments, juste pour vous assurer que vous ne compliquez pas trop votre code pour pas beaucoup d'avantages.

+0

Quand est-il invalidé? – Michael

+0

+1 Quant au moment où il est invalidé, beaucoup de choses peuvent l'invalider, et il est possible d'invalider seulement une partie de la vue (c'est pourquoi -drawRect: vous passe un CGRect pour vous dire quelle partie a été invalidée). Vous appelez -setNeedsDisplay l'invalide bien sûr. Lorsque la vue apparaît sur l'écran. Lorsque l'état de la vue change (en surbrillance par exemple). Lorsque la taille de la vue change. Parfois pendant le défilement (bien qu'il y ait quelques optimisations de cache bitmap là). –

+0

Dans d'autres commentaires, Apple vous déconseille de dessiner des dégradés si vous pouvez l'aider. L'iPhone n'est pas toujours assez rapide pour le faire. La recommandation d'Apple est d'utiliser des images. Les dégradés sont souvent traités avec des images d'un seul pixel qui sont ensuite étirées horizontalement pour remplir la zone souhaitée. –

0

Chaque UIView a son propre tampon de dessin hors-champ, donc en mouvement un UIView autour ou en modifiant sa visibilité ne fera pas redessiner l'UIView. Comme déjà mentionné, il existe des instances spécifiques qui vont déclencher un redraw, mais tant que vous ne les provoquerez pas, votre routine drawRect ne devrait pas être appelée. (À titre expérimental, essayez de démarrer votre routine drawRect avec un appel NSLog à regarder quand il est appelé.) Cela vous rapproche d'un dessin «rapide» - en limitant le nombre de fois que le dessin a lieu.

Vous devriez également être en mesure d'utiliser des outils de mesure de la performance comme Shark pour voir combien de temps vos routines de dessin prennent, et ce qui prend le plus de temps en leur sein. Cette analyse, en particulier, est utile pour ne pas mettre en cache quelque chose que vous pensez peut être coûteux alors que ce n'est pas le cas, ce qui vous occasionne du temps et de la complexité pour de très faibles performances.

Questions connexes