Domanda

Volevo chiarire qualcosa.

Diciamo che ho il seguente codice:

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

Ciò creerà 5 milioni di stringhe autolealizzate all'interno di questa chiamata di funzione. Mi aspettavo che questo mantenga quegli oggetti in giro fino alla fine dell'applicazione, poiché l'unico @AutoReleeSepool che vedo è quello che avvolge l'istanziazione dell'applicazione in Main.M. Questo non è il caso, però. Alla fine di questa funzione di funzione, sembra che tutti ottengano il loro rilascio e vengano rimossi dalla memoria.

Questo documento:

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

Afferma che "il kit di applicazioni crea un pool di autolesioni sul thread principale all'inizio di ogni ciclo del ciclo di eventi e lo drena alla fine, rilasciando così eventuali oggetti autolelati generati durante l'elaborazione di un evento."

Questo ha senso per me, ma questo è sotto Uikit e non il kit di applicazioni. La mia domanda è: Uikit/Cocoa Touch fa la stessa cosa in questo caso, o c'è un'altra spiegazione per il rilascio dei miei oggetti?

Grazie!

È stato utile?

Soluzione 2

Sì, Uikit fa la stessa cosa. Il pool di autolease del thread principale creato dal sistema viene drenato alla fine di ogni ciclo di ciclo di corsa. Probabilmente è meglio non fare affidamento su questa vita esatta nel tuo codice. Se si crea manualmente un nuovo thread (usando EG NSThread), sei responsabile della creazione del pool di autolesi su quel thread.

Modifica: la risposta di Rob fornisce alcune buone informazioni aggiuntive sul comportamento sotto ARC. In generale, è giusto dire che gli oggetti hanno meno probabilità di finire nel pool di autolesi a causa di alcune ottimizzazioni che l'arco è in grado di fare.

Altri suggerimenti

Andrew ha risposto alla tua domanda principale che, sì, il tuo pool di autolesioni sarà svuotato su ogni ciclo del ciclo di corsa principale. Quindi qualsiasi oggetto di autolelease creati in viewDidLoad Può essere pronta a drenare quando si cede al ciclo di corsa principale. Certamente non saranno mantenuti "fino alla risoluzione dell'applicazione".

Ma dovremmo stare attenti: stai chiaramente supponendo che questi oggetti vengano aggiunti a un pool di autolesi. Alcuni avvertimenti a questo presupposto:

  1. In passato (e ancora richiesto per l'interoperabilità ARC-MRC), quando si restituiscono oggetti da metodi i cui nomi non iniziavano con alloc, new, copy, o mutableCopy, quegli oggetti avrebbero autorelease oggetti, si sono accordati solo quando il pool di autolesso è stato drenato (cioè quando si è cessati al ciclo di corsa).

  2. Ma ARC è diventato più intelligente per ridurre al minimo la necessità di pool di autolesi (vedi http://rentzsch.tumblr.com/post/75082194868/arcs-fast-autolelease, che discute callerAcceptsFastAutorelease, ora chiamato callerAcceptsOptimizedReturn invocato da prepareOptimizedReturn), quindi spesso non vedrai questo autorelease comportamento. Quindi, se sia la libreria che il chiamante utilizzano l'arco, gli oggetti potrebbero non essere inseriti nel pool di autolesi, ma piuttosto l'arco li rilascerà abilmente immediatamente se non sono necessari.

    Con i progetti ARC contemporanei, i pool di autolesi non sono generalmente necessari. Ma alcuni casi speciali, si possono comunque beneficiare dell'utilizzo di pool di autolesi. Descriverò uno di quei casi di seguito.

Considera il seguente codice:

#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

Il seguente codice aggiungerà 500.000 oggetti al pool di autolelease, che verrà drenato solo quando cedo al ciclo di corsa:

no pool

In questo caso, è possibile utilizzare una piscina per autolesi per ridurre al minimo il marchio di acqua elevata:

@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

In conclusione, con l'arco, non è sempre ovvio quando ha usato un oggetto di autoleasico e quando lo rilascia esplicitamente quando la variabile cade dall'ambito. Puoi sempre confermarlo esaminando il comportamento negli strumenti.

A parte questo, sarei diffidente nel trarre troppe conclusioni di gestione della memoria generale quando si usa il NSString Classe, in quanto è stata altamente ottimizzata e non è sempre conforme alle pratiche di gestione della memoria standard.

Suppongo che quando assegni un nuovo oggetto a un riferimento che utilizzava un oggetto allora L'oggetto originale viene rilasciato subito (Se non ne indica nient'altro - il conteggio dei ref va a zero) usando l'arco e il default. strong Riferimento come nel tuo esempio di loop.

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 in Transizione alle note di rilascio dell'arco

Prova a smettere di pensare a dove vengono effettuate le chiamate di conservazione/rilascio e pensa invece agli algoritmi dell'applicazione. Pensa a puntatori "forti e deboli" nei tuoi oggetti, sulla proprietà degli oggetti e sui possibili cicli di consumo.

Sembra che il release viene chiamato su oggetto quando viene assegnato un nuovo valore, da Clang Clang 3.4 Documentation Obiettivo C Conteggio di riferimento automatico (ARC)

L'assegnazione si verifica quando si valuta un operatore di assegnazione. La semantica varia in base alla qualifica:

Per gli oggetti __strong, il nuovo punta viene mantenuto per la prima volta; In secondo luogo, il LVALUE è caricato con semantica primitiva; In terzo luogo, il nuovo punta è immagazzinato nel LVALUE con semantica primitiva; E infine, il vecchio punta viene rilasciato. Questo non viene eseguito atomicamente; La sincronizzazione esterna deve essere utilizzata per rendere questa sicura di fronte a carichi e negozi simultanei.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top