7

Unter Verwendung des Verfahrens NSObject -(id)awakeAfterUsingCoder:(NSCoder *)decoder als Beispiel sagt der Dokumentation:Was ist das neue Muster für die Freigabe von Selbst mit automatischer Referenzzählung?

Gibt einem Objekt, nachdem, dekodiert wird ein anderes Objekt für sich zu substituieren. Zum Beispiel kann ein Objekt, das eine Schriftart darstellt, nach der Decodierung von sich selbst freigeben und ein vorhandenes Objekt zurückgeben, das die gleiche Schriftbeschreibung wie die Schriftart hat. Auf diese Weise können redundante Objekte eliminiert werden.

Normalerweise würden Sie

[self release]; 
return substitutedObject; 

Mit ARC tun Sie diese Zeile aus verlassen. Würde das nicht lecken? Oder sollte ich nur dem NSCoder-Objekt vertrauen, um das ursprüngliche Objekt für mich freizugeben? Wenn ja, warum müssten Sie zuerst selbst mit Nicht-ARC-Code explizit freigeben?

Ich glaube nicht self = nil ist richtig in Anbetracht dessen, was der Compiler-Dokumentation sagt über sich selbst: http://clang.llvm.org/docs/AutomaticReferenceCounting.html#misc.self

Antwort

2

Wie bereits erwähnt, können Sie nicht [self release]; schreiben. Darüber hinaus ist awakeAfterUsingCoder:kein Initialisierer - Sie können self nicht neu zuweisen.

Würde dieses Leck nicht?

Ja. Im Programm unten nachgewiesen.

Oder sollte ich nur das NSCoder-Objekt vertrauen, um das ursprüngliche Objekt für mich freizugeben?

Nr

Ein Ansatz, um das Leck existiert unten - ich würde es nicht „das neue Muster“, nur der erste Ansatz, der in den Sinn kam. Es geht um eine explizite Freigabe von self und in diesem Fall eine explizites behalten des Ergebnisses:

#import <Foundation/Foundation.h> 

@interface MONBoolean : NSObject <NSCoding> 

- (id)initWithBool:(bool)pBool; 

- (bool)isTrue; 
- (bool)isFalse; 

@end 

static NSString * const MONBoolean_KEY_value = @"MONBoolean_KEY_value"; 

@implementation MONBoolean 
{ 
    bool value; 
} 

- (id)initWithBool:(bool)pBool 
{ 
    self = [super init]; 
    if (0 != self) { 
     value = pBool; 
    } 
    return self; 
} 

- (bool)isTrue 
{ 
    return true == value; 
} 

- (bool)isFalse 
{ 
    return false == value; 
} 

- (NSString *)description 
{ 
    return [[NSString alloc] initWithFormat:@"<%s:%p> : %s", object_getClassName(self), self, self.isTrue ? "true" : "false"]; 
} 

- (void)encodeWithCoder:(NSCoder *)aCoder 
{ 
    [aCoder encodeBool:value forKey:MONBoolean_KEY_value]; 
} 

- (id)initWithCoder:(NSCoder *)aDecoder 
{ 
    self = [super init]; 
    if (0 != self) { 
     value = [aDecoder decodeBoolForKey:MONBoolean_KEY_value]; 
    } 
    return self; 
} 

- (id)awakeAfterUsingCoder:(NSCoder *)aDecoder 
{ 
    const bool b = value; 
    // cannot reassign self outside of an initializer. 
    // if not released, will result in a leak: 
    CFRelease((__bridge const void*)self); 
    MONBoolean * result = [[MONBoolean alloc] initWithBool:b]; 
    // now we have to retain explicitly because this is 
    // an autoreleasing method: 
    CFRetain((__bridge const void*)result); 
    return result; 
} 

@end 

int main(int argc, const char * argv[]) 
{ 
    @autoreleasepool { 
     MONBoolean * a = [[MONBoolean alloc] initWithBool:true]; 
     NSData * data = [NSKeyedArchiver archivedDataWithRootObject:a]; 
     MONBoolean * b = [NSKeyedUnarchiver unarchiveObjectWithData:data]; 
     NSLog(@"%@", b); 
    } 
    system("leaks NAME_OF_PROCESS_HERE"); 
    return 0; 
} 
+0

Können Sie erklären, warum Sie das Ergebnis explizit beibehalten müssen? Sicher, das besiegt den Zweck von ARC. Danke, dass du alles andere beantwortet hast. – jamesmoschou

+0

@ moshy du bist willkommen. Ja - ich habe dieses Detail zunächst übersehen. Der Grund dafür ist, dass 'awakeAfterUsingCoder:' eine nicht-besitzende (oder automatisch freigegebene) Referenz zurückgibt. Daher fügt ARC für unseren Rückgabewert eine Ref-count-Dekrement für uns ein. Was wir tun wollen, ist effektiv * Transfer * die Referenz von einem Objekt zum anderen. Ich lief es in Instrumenten - keine Lecks. keine Zombies. ohne die explizite Zurückhaltung würde ein Zombie gemeldet werden. ohne die explizite Freigabe - ein Leck. Probieren Sie es selbst aus (setzen Sie den Block '@ autoreelease' in' while (1) '). – justin

+1

Könnten Sie die Methode mit '__attribute __ ((objc_method_family (init)))' zu einem Mitglied der 'init'-Familie machen und dann' self' neu zuweisen? –

0

Ich glaube, ARC intelligent genug, um den Überblick über alle Objekte zu halten. Sie sollten also nichts über Speicher sagen können und die App wird das Objekt freigeben, wenn es nicht mehr verwendet wird. Führen Sie es durch den Leckage-Profiler, nur für den Fall, aber es sollte in Ordnung sein.

+4

Aber mein Verständnis war, dass automatische Referenzzählung immer noch Referenzzählung ist. Es ist nicht dasselbe wie Garbage Collection. Daher ist es (theoretisch) immer noch möglich, undichte Objekte zu haben, wenn sie nicht mehr benutzt werden. Ich werde es durch den Profiler laufen lassen. – jamesmoschou

4

Ein ähnliches Problem im Zusammenhang mit den NIB Top-Level-Objekten unter Mac OS X stellt sich die Resource Programming Guide sagt:

Wenn der Besitzer ist die Datei nicht eine Instanz von NSWindowController oder NSViewController, dann müssen Sie den Referenzzähler dekrementieren von der obersten Ebene selbst Objekte. Bei der manuellen Referenzzählung konnte dies erreicht werden, indem Objekte der obersten Ebene eine release Nachricht gesendet wurde. Sie können dies nicht mit ARC tun. Stattdessen können Sie Verweise auf Objekte der obersten Ebene auf einen Core Foundation-Typ anwenden und CFRelease verwenden.

Also, diese Technik kann vermutlich auch in dieser Situation verwendet werden. CFRelease((__bridge CFTypeRef)self);

Verwandte Themen