6

Ich bin neu in ARC, aber verstehe, wie es funktioniert, und ich versuche es aus. Ich bin auf iOS, also ist Speicher ein ernstes Anliegen.Wie erzwinge Release auf iOS

Ich habe eine MyObject-Klasse, die viele große Daten enthält. Ich möchte es freigeben und einen neuen Datensatz laden.

MyObject *object; 
object = [[MyObject alloc] initWithData:folder1]; // load data from folder1 

// later... 
object = [[MyObject alloc] initWithData:folder2]; // load data from folder2 

Dies funktioniert ohne Lecks in Ordnung, und ich bin mit dem ARC Erraten fügt eine [object Release] vor der neuen Aufgabe. Mein Problem ist die Daten innerhalb 'Objekt' freigegeben nach der neue Satz ist zugeordnet, und ich habe nicht genug Speicher. Was ich wirklich möchte, ist:

object = nil; 

<function to pop the pool, wait till everything is deallocated> 

object = [MyObject alloc] initWithData:folder2]; // load data from folder2 

aber ich bin mir nicht sicher, wie das geht. Ich könnte die neue Zuweisung nach einer Verzögerung auf einem Performselektor durchführen, aber es fühlt sich an, als würde ich im Dunkeln drehen und ein bisschen hacken. Es gibt wahrscheinlich einen richtigen Weg, dies zu tun?

PS Ich habe versucht, nach einer Antwort zu suchen, aber alle Ergebnisse beziehen sich auf Speicherlecks und wie man sicherstellt, dass Variablen den Gültigkeitsbereich verlassen und Variablen auf Null setzen. Mein Problem ist nicht das, es ist mehr von a Timing Sache.

UPDATE
Danke für die Antworten, würde ich schon versucht

object = nil; 
object = [MyObject alloc] initWithData:folder2]; 

und es nicht funktioniert hat. Ich war mir nicht sicher, ob es sein sollte oder nicht. Jetzt verstehe ich, dass es ist soll funktionieren, aber ich muss etwas anderes halten für den Bruchteil einer Sekunde halten. Ich habe NSLogs in allen meinen init/dealloc-Methoden, und ich kann zuerst alle Inits der neuen Klasseninstanzen (von MyObjects-Ivars) aufrufen und dann fast unmittelbar nach (innerhalb von ein paar Millisekunden) die Dealloc von MyObject gefolgt von den Deallocs seiner Ivars. Ich habe auch die @autorelease ausprobiert, aber das Gleiche passiert.

Ich habe während des Projekts gesucht und den ganzen Code eingefügt, der für mich relevant sein könnte.

@interface AppDelegate : UIResponder <UIApplicationDelegate>; 
    @property PBSoundSession *soundSession; 
@end 


//-------------------------------------------------------------- 
@implementation AppDelegate 

// onTimer fired at 60Hz 
-(void)onTimer:(NSTimer *) theTimer { 
    [oscReceiver readIncoming]; // check incoming OSC messages 
    // then do a bunch of stuff with _soundSession; 
} 
@end 


//-------------------------------------------------------------- 
@implementation OscReceiver 

-(void)readIncoming { 
    AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate]; 

    // parse all incoming messages 

    if(bLoadNewSoundBank) { 
     NSString *newFolder = parseNewFolder(); 
     appDelegate.soundSession = nil; 
     appDelegate.soundSession = [MyObject alloc] initWithData:newFolder]; 
    } 
} 

@end 


//-------------------------------------------------------------- 
@implementation GuiController 

// onTimer fired at 10Hz 
-(void)onTimer:(NSTimer *) theTimer { 
    PBSoundSession *soundSession = appDelegate.soundSession; 
    // update gui with received values 
} 

@end 

Ich dachte, es könnte die ‚soundSession‘ lokaler Variable in dem GuiController sein :: OnTimer für die Dauer dieses Verfahrens auf die alte appDelegate.soundSession halten, aber zu meiner Überraschung all GUI-Code zu kommentieren out (den Timer zu deaktivieren, machte keinen Unterschied.

Gibt es eine Möglichkeit herauszufinden, wer noch an meiner appDelegate.soundSession festhält? Ich habe einen Haltepunkt platziert, wo ich ihn auf Null gesetzt habe, konnte aber keine nützlichen Informationen finden. Ich habe das Instrument in Allocation-Template ausprobiert, konnte dort aber auch nichts Nützliches finden (wahrscheinlich weil ich nicht weiß, wo ich hinschauen soll).

So sieht mein Zuordnungs-Track aus, Sie können sehen, dass der Speicher alle ein bisschen zu spät freigegeben wird! Valid XHTML.

+0

Nun, Sie würden viel von dem bekommen, was Sie wollen, indem Sie einfach "object = nil;" ausführen, bevor Sie das neue Objekt zuweisen/initialisieren. –

Antwort

10

Dies ist möglicherweise kein ARC-Problem. Was Sie sehen könnten ist, dass Ihre autorelease pool nicht zu früh drainiert - Ihr MyObject wird veröffentlicht, aber die Daten, die es geladen hat, werden vom Pool festgehalten, da einige interne -retain/-autorelease Paare vorhanden sind.Versuchen Sie, Ihre -initWithData: Anrufe in einem @autoreleasepool Block Einwickeln, wie folgt aus:

@autoreleasepool { 
    object = [[MyObject alloc] initWithData:folder1]; 
    // do things 
} 
// later… 
@autoreleasepool { 
    object = [[MyObject alloc] initWitData:folder2]; 
    // do other things 
} 

Einstellung des Objekts nil unmittelbar bevor es zu etwas Einstellung anders als Gabriele schlägt vor, könnte der Compiler veranlassen, die entsprechende Freigabe, bevor der zweite -alloc/-initWithData: einfügen , aber es könnte schlau genug sein, das schon zu tun - wenn das nicht funktioniert, ist es höchstwahrscheinlich die Autorelease-Pool-Sache.

+0

Ich überarbeitete meine Antwort. Sicherlich wird der Speicher freigegeben **, bevor der Zeiger neu zugewiesen wird, andernfalls tritt ein Speicherverlust auf, da es keinen Verweis auf diesen Teil des Speichers geben würde. –

+0

Richtig - ich sage, es ist möglich, dass das ** sofort ** vor der Neuzuweisung geschieht, dh nach dem zweiten '-alloc' /' -initWithData: ', aber bevor' object' auf das Ergebnis gesetzt wird, in diesem Fall dort wären momentan zwei MyObjects im Speicher. –

+0

Guter Punkt, ich muss darüber nachdenken –

3

Es gibt keine Verzögerung beim Entleeren eines @autoreleasepool {...}; Die Objekte im Pool haben sofort release aufgerufen. Wenn ein Objekt das überlebt, liegt es daran, dass es entweder eine strong Referenz an anderer Stelle gibt oder weil das Objekt autorelease d in den nächsten Pool out ist.

Wenn Sie das tun:

a = [[Foo alloc] initBigThing]; 
a = nil; 
a = [[Foo alloc] initBigThing]; 

Die erste Instanz von Foo wird vor der Zuteilung des zweiten

Mit einem großen Vorbehalt freigegeben werden; Wenn einer der Codepfade a bei retain/autorelease aufgerufen wird, wird es bleiben, bis der Pool leer ist. Umgeben es in @autoreleasepool{ ... }; sollte den Trick tun.

Beachten Sie, dass der Compiler manchmal retain/autorelease Sequenzen in nicht optimierten Builds ausgibt, die in optimierten Builds eliminiert werden.

1

ein bisschen mehr allgemeine Antwort, fand ich, wie Sie ein Objekt freigeben zwingen kann:

#import <objc/message.h> 

// --- 

while ([[object valueForKey:@"retainCount"] integerValue] > 1) { 
    objc_msgSend(object, NSSelectorFromString(@"release")); 
} 
objc_msgSend(object, NSSelectorFromString(@"release")); 

Aber Sie sollten dies nicht tun, weil ARC wahrscheinlich später das Objekt freigeben wird, und dies wird zu einem Absturz führen. Diese Methode sollte nur im Debug verwendet werden!

Verwandte Themen