41 votes

Avec l'ARC, qu'est-ce qui est le mieux : alloc ou initialisateurs autorelease ?

Est-il préférable (plus rapide & plus efficace) d'utiliser alloc o autorelease initialisateurs. Par exemple :

- (NSString *)hello:(NSString *)name {
    return [[NSString alloc] initWithFormat:@"Hello, %@", name];
}

OU

- (NSString *)hello:(NSString *)name {
    return [NSString stringWithFormat:@"Hello, %@", name];
//    return [@"Hello, " stringByAppendingString:name]; // even simpler
}

Je sais que dans la plupart des cas, les performances ici ne devraient pas avoir d'importance. Mais j'aimerais quand même prendre l'habitude de le faire de la meilleure façon.

Si elles font exactement la même chose, je préfère la seconde option car elle est plus courte à taper et plus lisible.

Dans Xcode 4.2, y a-t-il un moyen de voir ce vers quoi ARC compile, c'est-à-dire où il met retain , release , autorelease etc ? Cette fonctionnalité serait très utile lors du passage à l'ARC. Je sais que vous ne devriez pas avoir à penser à ce genre de choses, mais cela m'aiderait à trouver la réponse à des questions comme celles-ci.

37voto

Stuart Carnie Points 2961

La différence est subtile, mais vous devriez opter pour le autorelease versions. Premièrement, votre code est beaucoup plus lisible. Deuxièmement, à l'inspection de la sortie de l'assemblage optimisé, l'élément autorelease est légèrement plus optimale.

El autorelease version,

- (NSString *)hello:(NSString *)name {
    return [NSString stringWithFormat:@"Hello, %@", name];
}

se traduit par

"-[SGCAppDelegate hello:]":
    push    {r7, lr}
    movw    r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_-(LPC0_0+4))
    mov r3, r2
    movt    r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_-(LPC0_0+4))
    movw    r0, :lower16:(L_OBJC_CLASSLIST_REFERENCES_$_-(LPC0_1+4))
    movt    r0, :upper16:(L_OBJC_CLASSLIST_REFERENCES_$_-(LPC0_1+4))
    add r1, pc
    add r0, pc
    mov r7, sp
    ldr r1, [r1]
    ldr r0, [r0]
    movw    r2, :lower16:(L__unnamed_cfstring_-(LPC0_2+4))
    movt    r2, :upper16:(L__unnamed_cfstring_-(LPC0_2+4))
    add r2, pc
    blx _objc_msgSend    ; stringWithFormat:
    pop {r7, pc}

Alors que la version [[alloc] init] ressemble à ce qui suit :

"-[SGCAppDelegate hello:]":
    push    {r4, r5, r6, r7, lr}
    movw    r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_2-(LPC1_0+4))
    add r7, sp, #12
    movt    r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_2-(LPC1_0+4))
    movw    r0, :lower16:(L_OBJC_CLASSLIST_REFERENCES_$_-(LPC1_1+4))
    movt    r0, :upper16:(L_OBJC_CLASSLIST_REFERENCES_$_-(LPC1_1+4))
    add r1, pc
    add r0, pc
    ldr r5, [r1]
    ldr r6, [r0]
    mov r0, r2
    blx _objc_retain    ; ARC retains the name string temporarily
    mov r1, r5
    mov r4, r0
    mov r0, r6
    blx _objc_msgSend   ; call to alloc
    movw    r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_4-(LPC1_2+4))
    mov r3, r4
    movt    r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_4-(LPC1_2+4))
    add r1, pc
    ldr r1, [r1]
    movw    r2, :lower16:(L__unnamed_cfstring_-(LPC1_3+4))
    movt    r2, :upper16:(L__unnamed_cfstring_-(LPC1_3+4))
    add r2, pc
    blx _objc_msgSend   ; call to initWithFormat:
    mov r5, r0
    mov r0, r4
    blx _objc_release   ; ARC releases the name string
    mov r0, r5
    pop.w   {r4, r5, r6, r7, lr}
    b.w _objc_autorelease

Comme prévu, il est un peu plus long, parce qu'il fait appel à la fonction alloc y initWithFormat: méthodes. Ce qui est particulièrement intéressant, c'est que l'ARC génère ici un code sous-optimal, puisqu'il conserve l'option name (noté par l'appel à _objc_retain) et libéré plus tard après l'appel à _objc_retain_. initWithFormat: .

Si nous ajoutons le __unsafe_unretained comme dans l'exemple suivant, le code est rendu de manière optimale. __unsafe_unretained indique au compilateur d'utiliser une primitive (pointeur de copie) sémantique des affectations .

- (NSString *)hello:(__unsafe_unretained NSString *)name {
    return [[NSString alloc] initWithFormat:@"Hello, %@", name];
}

comme suit :

"-[SGCAppDelegate hello:]":
    push    {r4, r7, lr}
    movw    r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_2-(LPC1_0+4))
    add r7, sp, #4
    movt    r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_2-(LPC1_0+4))
    movw    r0, :lower16:(L_OBJC_CLASSLIST_REFERENCES_$_-(LPC1_1+4))
    movt    r0, :upper16:(L_OBJC_CLASSLIST_REFERENCES_$_-(LPC1_1+4))
    add r1, pc
    add r0, pc
    mov r4, r2
    ldr r1, [r1]
    ldr r0, [r0]
    blx _objc_msgSend
    movw    r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_4-(LPC1_2+4))
    mov r3, r4
    movt    r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_4-(LPC1_2+4))
    add r1, pc
    ldr r1, [r1]
    movw    r2, :lower16:(L__unnamed_cfstring_-(LPC1_3+4))
    movt    r2, :upper16:(L__unnamed_cfstring_-(LPC1_3+4))
    add r2, pc
    blx _objc_msgSend
    .loc    1 31 1
    pop.w   {r4, r7, lr}
    b.w _objc_autorelease

10voto

Tammo Freese Points 5648

[NSString stringWithFormat:] est moins de code. Mais soyez conscient que l'objet peut se retrouver dans le pool d'autorelease. Et cela se produit actuellement même avec l'optimisation du compilateur ARC et -Os.

Actuellement, les performances de [[NSString alloc] initWithFormat:] est meilleur sur iOS (testé avec iOS 5.1.1 et Xcode 4.3.3) et OS X (testé avec OS X 10.7.4 et Xcode 4.3.3). J'ai modifié l'exemple de code de @Pascal pour inclure les temps de vidange automatique des piscines et j'ai obtenu les résultats suivants :

  • L'optimisation ARC n'empêche pas les objets de se retrouver dans le pool d'autorelease.

  • Y compris le temps pour vider le pool de libération avec 1 million d'objets, [[NSString alloc] initWithFormat:] est environ 14% plus rapide sur l'iPhone 4S, et environ 8% plus rapide sur OS X

  • Avoir un @autoreleasepool autour de la boucle libère tous les objets à la fin de la boucle, ce qui consomme beaucoup de mémoire.

    Instruments showing memory spikes for  NSString stringWithFormat:  and not for   NSString alloc  initWithFormat:  on iOS 5.1

  • Les pics de mémoire peuvent être évités en utilisant un @autoreleasepool à l'intérieur de la boucle. Les performances restent à peu près les mêmes, mais la consommation de mémoire est alors plate.

4voto

Mike Points 489

Je ne suis pas d'accord avec les autres réponses, la version autorelease (votre 2ème exemple) n'est pas nécessairement meilleure.

La version autorelease se comporte exactement comme avant ARC. Elle alloue et init et ensuite autorelease, ce qui signifie que le pointeur vers l'objet doit être stocké pour être autoreleased plus tard, la prochaine fois que le pool autorelease est vidé. Cela utilise un peu plus de mémoire car le pointeur vers cet objet doit être conservé jusqu'à ce qu'il soit traité. L'objet reste également en place plus longtemps que s'il était immédiatement libéré. Cela peut être un problème si vous appelez cette fonction plusieurs fois dans une boucle, de sorte que le pool d'autorelease n'a pas la possibilité d'être vidé. Cela pourrait vous amener à manquer de mémoire.

Le premier exemple se comporte différemment de ce qu'il était avant l'ARC. Avec ARC, le compilateur insère désormais un "release" pour vous (PAS un autorelease comme dans le deuxième exemple). Il le fait à la fin du bloc où la mémoire est allouée. Habituellement, c'est à la fin de la fonction où elle est appelée. Dans votre exemple, en regardant l'assemblage, il semble que l'objet soit en fait autorelease. Cela pourrait être dû au fait que le compilateur ne sait pas où la fonction retourne et donc où se trouve la fin du bloc. Dans la majorité des cas où un release est ajouté par le compilateur à la fin d'un bloc, la méthode alloc/init donnera de meilleures performances, du moins en termes d'utilisation de la mémoire, comme c'était le cas avant ARC.

3voto

Pascal Points 8222

Eh bien, c'est quelque chose de facile à tester, et en effet il semble que le constructeur de commodité est "plus rapide" -- à moins que j'ai fait une erreur dans mon code de test, voir ci-dessous.

Sortie (Temps pour 1 million de constructions)

Alloc/init:   842.549473 millisec
Convenience:  741.611933 millisec
Alloc/init:   799.667462 millisec
Convenience:  741.814478 millisec
Alloc/init:   821.125221 millisec
Convenience:  741.376502 millisec
Alloc/init:   811.214693 millisec
Convenience:  795.786457 millisec

script

#import <Foundation/Foundation.h>
#import <mach/mach_time.h>

int main (int argc, const char * argv[])
{

    @autoreleasepool {
        NSUInteger runs = 4;

        mach_timebase_info_data_t timebase;
        mach_timebase_info(&timebase);
        double ticksToNanoseconds = (double)timebase.numer / timebase.denom;

        NSString *format = @"Hello %@";
        NSString *world = @"World";

        NSUInteger t = 0;
        for (; t < 2*runs; t++) {
            uint64_t start = mach_absolute_time();
            NSUInteger i = 0;
            for (; i < 1000000; i++) {
                if (0 == t % 2) {       // alloc/init
                    NSString *string = [[NSString alloc] initWithFormat:format, world];
                }
                else {                  // convenience
                    NSString *string = [NSString stringWithFormat:format, world];
                }
            }
            uint64_t run = mach_absolute_time() - start;
            double runTime = run * ticksToNanoseconds;

            if (0 == t % 2) {
                NSLog(@"Alloc/init:   %.6f millisec", runTime / 1000000);
            }
            else {
                NSLog(@"Convenience:  %.6f millisec", runTime / 1000000);
            }
        }
    }
    return 0;
}

0voto

Andrea Bergia Points 3346

Je pense que l'implémentation de stringWithFormat : est en fait implémentée comme votre 1ère version, ce qui signifie que rien ne devrait changer. En tout cas, s'il y a une différence, il semble probablement que la deuxième version ne devrait pas être plus lente. Enfin, à mon avis, la seconde version est légèrement plus lisible, c'est donc celle que j'utiliserais.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X