Question

Je voulais faire clarifier quelque chose.

Disons que j'ai le code suivant:

- (void) viewDidAppear:(BOOL)animated {
  [super viewDidAppear:animated];
  for (int i = 0; i < 5000000; i++) {
    NSString *s = [NSString stringWithFormat:@"Hello, %@!", @"World"];
  }
}

Cela créera 5 millions de chaînes d'autore élue dans cet appel de fonction. Je m'attendais à ce que cela garde ces objets autour jusqu'à la fin de l'application, car le seul @AutoreleSelepool que je vois est celui qui enveloppe l'instanciation de l'application dans Main.m. Ce n'est pas le cas, cependant. À la fin de cet appel de fonction, il semble qu'ils obtiennent tous leur version appelée et sont supprimés de la mémoire.

Ce document:

https://developer.apple.com/library/mac/documentation/cocoa/reference/foundation/classes/nsautoreleasepool_class/reference/reference.html

Indique que "le kit d'application crée un pool d'autorease sur le fil principal au début de chaque cycle de la boucle d'événement, et le draine à la fin, libérant ainsi tous les objets automatiquement générés lors du traitement d'un événement".

Cela a du sens pour moi, mais c'est sous Uikit, et non le kit d'application. Ma question est la suivante: Uikit / Cocoa Touch fait-il la même chose dans ce cas, ou y a-t-il une autre explication à la libération de mes objets?

Merci!

Était-ce utile?

La solution 2

Oui, Uikit fait la même chose. Le pool de filetage principal créé par le système est drainé à la fin de chaque cycle de boucle d'exécution. Il est probablement préférable de ne pas compter sur cette durée de vie exacte dans votre propre code. Si vous créez un nouveau thread manuellement (en utilisant par exemple nsthread), vous êtes responsable de la création du pool Autorelease sur ce fil.

EDIT: La réponse de Rob fournit de bonnes informations supplémentaires concernant le comportement sous ARC. En général, il est juste de dire que les objets sont moins susceptibles de se retrouver dans le pool d'autorelease en raison de certaines optimisations que l'arc est capable de faire.

Autres conseils

Andrew a répondu à votre principale question selon laquelle, oui, votre piscine d'autorease sera drainée à chaque cycle de la boucle de course principale. Donc, tous les objets d'autorease créés dans viewDidLoad Peut être rapidement vidé lorsque vous revenez à la boucle de course principale. Ils ne seront certainement pas conservés "avant la résiliation de la demande".

Mais nous devons être prudents: vous supposez clairement que ces objets sont ajoutés à un pool d'autorease. Quelques mises en garde à cette hypothèse:

  1. Dans le passé (et toujours requis pour l'interopérabilité ARC-MRC), lors du retour d'objets de méthodes dont les noms n'ont pas commencé alloc, new, copy, ou mutableCopy, ces objets n'auraient eu des objets d'autorease, ne traitent que lorsque le pool d'autorease était drainé (c'est-à-dire lorsque vous avez renoncé à la boucle d'exécution).

  2. Mais l'arc est devenu plus intelligent de minimiser le besoin de piscines d'autorease (voir http://rentzsch.tumblr.com/post/75082194868/arcs-fast-autorelease, qui discute callerAcceptsFastAutorelease, maintenant appelé callerAcceptsOptimizedReturn invoqué par prepareOptimizedReturn), donc vous ne verrez souvent pas cela autorelease comportement. Ainsi, si la bibliothèque et l'appelant utilisent l'arc, les objets peuvent ne pas être placés dans le pool d'autorelease, mais l'arc les libérera intelligemment immédiatement s'ils ne sont pas nécessaires.

    Avec des projets d'arc contemporains, les pools d'autorease ne sont généralement pas nécessaires. Mais certains cas spéciaux, on peut toujours bénéficier de l'utilisation de pools d'autorease. Je décrirai l'un de ces cas ci-dessous.

Considérez le code suivant:

#import "ViewController.h"
#import <sys/kdebug_signpost.h>

typedef enum : NSUInteger {
    InnerLoop = 1,
    MainLoop = 2
} MySignPostCodes;

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSURL *fileURL = [[NSBundle mainBundle] URLForResource:@"test" withExtension:@"png"];
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        kdebug_signpost_start(MainLoop, 0, 0, 0, 1);
        for (int j = 0; j < 500; i++) {
            NSData *data = [NSData dataWithContentsOfURL:fileURL];
            UIImage *image = [[UIImage alloc] initWithData:data];
            NSLog(@"%p", NSStringFromCGSize(image.size));  // so it's not optimized out
            [NSThread sleepForTimeInterval:0.01];
        }
        kdebug_signpost_end(MainLoop, 0, 0, 0, 1);
    });
}

@end

Le code suivant ajoutera 500 000 objets au pool d'autorelease, qui ne sera drainé que lorsque je reviendrai à la boucle d'exécution:

no pool

Dans ce cas, vous pouvez utiliser une piscine d'autorease pour minimiser la marque élevée de l'eau:

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSURL *fileURL = [[NSBundle mainBundle] URLForResource:@"test" withExtension:@"png"];
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        kdebug_signpost_start(MainLoop, 0, 0, 0, 1);
        for (int j = 0; j < 5; j++) {
            @autoreleasepool {
                kdebug_signpost_start(InnerLoop, 0, 0, 0, 2);
                for (long i = 0; i < 100; i++) {
                    NSData *data = [NSData dataWithContentsOfURL:fileURL];
                    UIImage *image = [[UIImage alloc] initWithData:data];
                    NSLog(@"%p", NSStringFromCGSize(image.size));  // so it's not optimized out
                    [NSThread sleepForTimeInterval:0.01];
                }
                kdebug_signpost_end(InnerLoop, 0, 0, 0, 2);
            }
        }
        kdebug_signpost_end(MainLoop, 0, 0, 0, 1);
    });
}

@end

pool

En bout de ligne, avec l'arc, il n'est pas toujours évident lorsqu'il a utilisé un objet Autorelease et lorsqu'il le libère explicitement lorsque la variable tombe de la portée. Vous pouvez toujours le confirmer en examinant le comportement dans les instruments.

En passant, je me méfierais de tirer trop de conclusions générales de gestion de la mémoire lors de l'utilisation du NSString classe, car elle a été très optimisée et n'est pas toujours conforme aux pratiques de gestion de la mémoire standard.

Je suppose que lorsque vous attribuez un nouvel objet à une référence utilisée pour maintenir un objet alors L'objet original est libéré tout de suite (Si rien d'autre ne le montre - Ref Count va à zéro) en utilisant l'arc et un défaut asument strong référence comme dans votre exemple de boucle.

MyObject *object = [[MyObject alloc] init]; // obj1, ref count 1 because strong
object = [[MyObject alloc] init]; // obj2, ref count of obj1 should be 0
                                  // so obj1 gets released

Apple note dans Transition vers les notes de libération d'arc

Essayez d'arrêter de réfléchir à l'endroit où les appels de conservation / relâchent et pensez à vos algorithmes d'application à la place. Pensez à des pointeurs «forts et faibles» dans vos objets, à la propriété des objets et à des cycles de conservation possibles.

Il semble que le release est appelé objet lorsqu'il est attribué à une nouvelle valeur, de Clang Clang 3.4 Documentation Objective-C Compte de référence automatique (ARC)

L'attribution se produit lors de l'évaluation d'un opérateur d'affectation. La sémantique varie en fonction de la qualification:

Pour les objets __strong, le nouveau pointee est d'abord conservé; Deuxièmement, la LVALUE est chargée de sémantique primitive; Troisièmement, le nouveau pointeur est stocké dans la Lvalue avec une sémantique primitive; Et enfin, l'ancien pointe est libéré. Ceci n'est pas effectué atomiquement; La synchronisation externe doit être utilisée pour rendre cela sûr face à des charges et des magasins simultanés.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top