2014-07-25 6 views
7

Ich habe Testcode wie dieseserstellt NSThread jetzt automatisch einen automatischen Löschpool?

- (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]); 
} 

ich keine autoreleasepool für meine eigenen Thread erstellt haben, aber wenn der Fadenausgang, Objekt „mein“ dealloc.why gerade?

obwohl ich meinen Test-Code zu ändern, wie unten

- (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]); 
} 

ich meine eigene autoreleasepool schaffen, aber es nicht abfließen, wenn der Fadenausgang. Objekt "mein" kann trotzdem trotzdem dealloc werden. Warum?

Ich benutze Xcode5 und mit ARC nicht

+0

Wie sagen Sie, dass Ihr mein Objekt freigegeben wird? –

+0

können Sie die erforderlichen Informationen erhalten? –

+0

weil ich einen Haltepunkt in Objekt "meine" Dealloc-Methode – eliudnis

Antwort

1

Apple documentation says (4 th Absatz):

Sie ein NSAutoreleasePool Objekt mit dem üblichen alloc erstellen und init Nachrichten und entsorgen Sie es mit Drain (oder release - den Unterschied zu verstehen, siehe Garbage Collection). Da Sie einen Autorelease-Pool nicht behalten können (oder ihn automatisch freigeben, siehe Retain und Autorelease), hat das Löschen eines Pools letzten Endes den Effekt, die Zuordnung aufzuheben. Sie sollten immer einen Autorelease-Pool im selben Kontext (Aufruf einer Methode oder Funktion oder Körper einer Schleife), die es erstellt wurde. Weitere Informationen finden Sie unter Verwenden von Pools für die automatische Freigabe.

16

Es ist nicht dokumentiert, aber die Antwort erscheint Ja, auf OS X 10.9+ und iOS 7+ zu sein.

Die Objective-C-Laufzeit ist open-source, so dass Sie die Quelle lesen können, um zu sehen, was vor sich geht. Die neueste Version der Laufzeitumgebung (646, die mit OS X 10.10 und iOS 8 ausgeliefert wurde) fügt tatsächlich einen Pool hinzu, wenn Sie autorelease ohne einen Pool im aktuellen Thread ausführen. 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); 
} 

Diese Funktion wird aufgerufen, wenn Sie den ersten Pool schieben (in diesem Fall schob die Sache ist POOL_SENTINEL), oder Sie ohne Pool Autorelease. Wenn der erste Pool gedrückt wird, richtet er den Autorelease-Stack ein. Aber wie Sie aus dem Code sehen, solange die Umgebungsvariable DebugMissingPools nicht gesetzt ist (es ist nicht standardmäßig eingestellt), wenn Autorelease ohne Pool gemacht wird, richtet sie auch den Autorelease-Stack ein, und schiebt dann einen Pool (drückt a POOL_SENTINEL).

Ähnlich (es ist ein wenig schwer zu folgen, ohne auf den anderen Code zu schauen, aber das ist der relevante Teil), wenn der Thread zerstört wird (und der Thread-Local Storage zerstört wird), gibt es alles im Autorelease-Stack (das ist, was die pop(0); der Fall ist), so dass es verlässt sich nicht auf dem Benutzer den letzten Pool Pop:

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

die vorherige Version der Common Language Runtime (551.1, die mit OS kam X 10.9 und iOS 7), auch tat dies, wie Sie von seinem NSObject.mm sehen können:

Aber die vorherige Version (532.2, die mit OS X 10 kam.8 und 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); 
} 

Beachten Sie, dass die oben genannten Arbeiten für alle pthread s, nicht nur NSThread s.

Also im Grunde, wenn Sie auf OS X 10.9+ oder iOS 7+ laufen, sollte Autoreleasing auf einem Thread ohne einen Pool nicht zu einem Leck führen. Dies ist nicht dokumentiert und stellt ein internes Implementierungsdetail dar. Seien Sie also vorsichtig, wenn Sie sich darauf verlassen, da Apple es in einem zukünftigen Betriebssystem ändern könnte. Ich sehe jedoch keinen Grund, warum sie diese Funktion entfernen würden, da sie einfach ist und nur Vorteile und keine Nachteile hat, es sei denn, sie schreiben die Art und Weise, wie Autorelease-Pools funktionieren oder etwas anderes, komplett um.

+0

Hervorragende Antwort. Großartige Detektivarbeit. Danke fürs Schreiben. (Voted) –

+1

Beachten Sie, dass die automatische Bereinigung * nicht * für den Hauptthread geschieht. Die Aufräumfunktion 'tls_dealloc' ist ein Thread-Local-Storage (TLS) -Destruktor, der durch einen Aufruf von' pthread_key_init_np' hinzugefügt wird. Diese Destruktoren werden von 'pthread_exit' aufgerufen, das implizit aufgerufen wird, wenn ein Thread von seiner Startroutine zurückkehrt, aber nicht im Fall des Hauptthreads. Daher wird der implizite Pool, der von 'autoreleaseNoPage' hinzugefügt wurde, nicht gelöscht. Siehe http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_exit.html –

Verwandte Themen