2014-07-25 13 views
7

Ho codice test come questoNSThread crea automaticamente autoreleasepool ora?

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(test) object:nil]; 
    [thread start]; 
} 

-(void)test 
{ 
    MyClass *my = [[[MyClass alloc] init] autorelease]; 
    NSLog(@"%@",[my description]); 
} 

Non ho creato alcun autoreleasepool per il mio filo, ma quando l'uscita, oggetto thread "mio" solo dealloc.why?

anche se cambio il mio codice di prova, come di seguito

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 

    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(test) object:nil]; 
    [thread start]; 
} 

-(void)test 
{ 
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 
    MyClass *my = [[[MyClass alloc] init] autorelease]; 
    NSLog(@"%@",[my description]); 
} 

creo la mia autoreleasepool ma non scolo quando l'uscita thread. l'oggetto "mio" può ancora dealloc comunque. perché?

Io uso Xcode5 e non utilizzando ARC

+0

Come stai dicendo che il mio oggetto viene rilasciato? –

+0

sei in grado di ottenere le informazioni richieste? –

+0

perché ho un punto di interruzione nell'oggetto metodo dealloc "mio" – eliudnis

risposta

1

Apple documentation says (4 ° comma):

Si crea un oggetto NSAutoreleasePool con la solita alloc e messaggi init e smaltirlo con scarico (o release-to capire la differenza, vedere Garbage Collection). Dal momento che non è possibile mantenere un pool autorelease (o autorizzarlo automaticamente - vedere conservare e autorelease), lo scarico di un pool ha l'effetto di deallocarlo. È necessario che scarichi sempre un pool di autorelease nello stesso contesto (invocazione di un metodo o funzione o corpo di un loop) che è stato creato . Vedi Usare i blocchi del pool di Autorelease per maggiori dettagli.

16

Non è documentato, ma la risposta sembra essere sì , su OS X 10.9+ e iOS 7+.

Il runtime Objective-C è open-source in modo da poter leggere il codice sorgente per vedere cosa sta succedendo. L'ultima versione del runtime (646, fornita con OS X 10.10 e iOS 8) aggiunge effettivamente un pool se si esegue uno autorelease senza un pool sul thread corrente. In NSObject.mm:

static __attribute__((noinline)) 
id *autoreleaseNoPage(id obj) 
{ 
    // No pool in place. 
    assert(!hotPage()); 

    if (obj != POOL_SENTINEL && DebugMissingPools) { 
     // We are pushing an object with no pool in place, 
     // and no-pool debugging was requested by environment. 
     _objc_inform("MISSING POOLS: Object %p of class %s " 
        "autoreleased with no pool in place - " 
        "just leaking - break on " 
        "objc_autoreleaseNoPool() to debug", 
        (void*)obj, object_getClassName(obj)); 
     objc_autoreleaseNoPool(obj); 
     return nil; 
    } 

    // Install the first page. 
    AutoreleasePoolPage *page = new AutoreleasePoolPage(nil); 
    setHotPage(page); 

    // Push an autorelease pool boundary if it wasn't already requested. 
    if (obj != POOL_SENTINEL) { 
     page->add(POOL_SENTINEL); 
    } 

    // Push the requested object. 
    return page->add(obj); 
} 

Questa funzione viene chiamata quando si preme la prima piscina (nel qual caso la cosa ha spinto è POOL_SENTINEL), oppure si autorelease senza piscina. Quando viene premuto il primo pool, imposta lo stack di autorelease. Ma come si vede dal codice, purché la variabile ambientale DebugMissingPools non sia impostata (non è impostata per impostazione predefinita), quando l'autorelease viene eseguito senza pool, imposta anche lo stack di autorelease e quindi spinge un pool (spinge un POOL_SENTINEL).

Analogamente, (è un po 'difficile da seguire senza guardare l'altro codice, ma questa è la parte rilevante) quando il thread viene distrutto (e il Thread-Local Storage è distrutto (che è ciò che il pop(0); fa) in modo che non si basa su l'utente al pop all'ultimo piscina:

static void tls_dealloc(void *p) 
{ 
    // reinstate TLS value while we work 
    setHotPage((AutoreleasePoolPage *)p); 
    pop(0); 
    setHotPage(nil); 
} 

la precedente versione del runtime (551.1, che è venuto con OS X 10.9 e iOS 7), anche ha fatto questo, come potete vedere dal suo NSObject.mm:

static __attribute__((noinline)) 
id *autoreleaseSlow(id obj) 
{ 
    AutoreleasePoolPage *page; 
    page = hotPage(); 

    // The code below assumes some cases are handled by autoreleaseFast() 
    assert(!page || page->full()); 

    if (!page) { 
     // No pool. Silently push one. 
     assert(obj != POOL_SENTINEL); 

     if (DebugMissingPools) { 
      _objc_inform("MISSING POOLS: Object %p of class %s " 
         "autoreleased with no pool in place - " 
         "just leaking - break on " 
         "objc_autoreleaseNoPool() to debug", 
         (void*)obj, object_getClassName(obj)); 
      objc_autoreleaseNoPool(obj); 
      return nil; 
     } 

     push(); 
     page = hotPage(); 
    } 

    do { 
     if (page->child) page = page->child; 
     else page = new AutoreleasePoolPage(page); 
    } while (page->full()); 

    setHotPage(page); 
    return page->add(obj); 
} 

Ma la versione precedente (532.2, fornita con OS X 10.8 e iOS 6), does not:

static __attribute__((noinline)) 
id *autoreleaseSlow(id obj) 
{ 
    AutoreleasePoolPage *page; 
    page = hotPage(); 

    // The code below assumes some cases are handled by autoreleaseFast() 
    assert(!page || page->full()); 

    if (!page) { 
     assert(obj != POOL_SENTINEL); 
     _objc_inform("Object %p of class %s autoreleased " 
        "with no pool in place - just leaking - " 
        "break on objc_autoreleaseNoPool() to debug", 
        obj, object_getClassName(obj)); 
     objc_autoreleaseNoPool(obj); 
     return NULL; 
    } 

    do { 
     if (page->child) page = page->child; 
     else page = new AutoreleasePoolPage(page); 
    } while (page->full()); 

    setHotPage(page); 
    return page->add(obj); 
} 

Nota che i lavori di cui sopra per eventuali pthread s, non solo NSThread s.

Quindi, in pratica, se si utilizza OS X 10.9+ o iOS 7+, l'esecuzione automatica su un thread senza pool non dovrebbe causare perdite. Questo non è documentato ed è un dettaglio di implementazione interno, quindi fai attenzione a fare affidamento su ciò in quanto Apple potrebbe cambiarlo in un sistema operativo futuro. Tuttavia, non vedo alcun motivo per cui rimuovono questa funzionalità in quanto è semplice e ha solo vantaggi e svantaggi, a meno che non vengano completamente riscritti nel modo in cui i pool di autorelease funzionano o qualcosa del genere.

+0

Risposta eccezionale. Grande lavoro investigativo. Grazie per la pubblicazione. (Votato) –

+1

Si noti che la pulizia automatica non * avviene * per il thread principale. La funzione di pulizia 'tls_dealloc' è un distruttore di thread-local-storage (TLS), aggiunto da una chiamata a' pthread_key_init_np'. Questi distruttori sono chiamati da 'pthread_exit', che viene chiamato implicitamente quando un thread ritorna dalla sua routine di avvio, ma non nel caso del thread principale. Quindi il pool implicito aggiunto da 'autoreleaseNoPage' non verrà svuotato. Vedi http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_exit.html –