Ich habe eine kreisförmige Referenz. Wie kann ich einen schwachen Verweis in Objective-C erstellen?
-
21-08-2019 - |
Frage
Ich arbeite an einer iPhone-Applikation. Ich habe ein Objekt der Klasse Row
, die zahlreiche Objekte der Klasse Block
lösen muss. Jeder Block
verfügt derzeit über eine Eigenschaft, die eine Instanzvariable der Klasse Row
behält.
@interface Block : UIImageView {
Row *yCoord;
}
@property (nonatomic,retain) Row *yCoord;
@end
Jeder Row
enthält eine NSMutableArray
dieser Blöcke.
@interface Row : NSObject {
NSMutableArray *blocks;
}
-(void)addBlock:(Block*)aBlock;
@end
@implementation Row
-(void)addBlock:(Block*)aBlock {
[blocks addObject:aBlock];
aBlock.yCoord = self;
}
@end
Ich verstehe, dass dies eine kreisförmige Referenz. Apples Dokumentation besagt, dass, um ein Objekt mit einer kreisförmigen Referenz ausplanen Ich brauche eine schwache Referenz statt eine starke Referenz (a behalten Eigenschaft), aber es durch nicht folgen und erklärt, wie genau gehe ich so tun. Ich plane, alle Blöcke innerhalb einer Zeile zu lösen und dealloc sowie die Reihe selbst gleichzeitig. Wie kann ich einen schwachen Verweis in jedem meiner Blocks ihrer „Eltern“ Row up?
Lösung
Edit: Da die Fragesteller hat er nicht Garbage Collection geklärt ist mit (iPhone derzeit nicht unterstützt), mein Rat ist, Zyklen zu vermeiden, indem nur eine der Aufgaben, die andere behalten, wie Sie mit einem Delegierten tun würde. Wenn Eigenschaften, verwenden Sie „zuweisen“ statt „behalten“, dies zu erreichen. Zum Beispiel:
@property (nonatomic,assign) Row *yCoord;
Der Rest meiner Antwort Antwort bezieht sich auf "schwache Verweise" in Bezug auf die Objective-C 2.0 und GC.
Wenn Sie mit der Garbage Collection (10.5 +) arbeiten, wird ein schwacher Verweis wird durch das Vorsetzen eine Variablendeklaration mit erstellt __weak
. Wenn Sie dieser Variablen zugewiesen, die GC (wenn aktiviert) hält der Referenzspur und wird es automatisch für Sie Null aus, wenn alle starken Verweise auf das referenzierte Objekt verschwinden. (Wenn GC nicht aktiviert ist, das __weak
Attribut wird ignoriert.)
So können Sie sicher die obige Antwort ändern mit Garbage Collection spielen schöneren (aktuell 10.5 + und vielleicht einen Tag auf dem iPhone) wie folgt: (Siehe im Zusammenhang Apple-docs .)
@property (nonatomic,assign) __weak Row *yCoord;
Chris Hanson (wo Sie nähere Informationen finden):
„Durch eine Instanz Variablendeklaration mit
__weak
prefixing, der Garbage Collector sagen, dass, wenn es der einzige Verweis auf ein Objekt, dass das Objekt sollte sammelbaren in Betracht gezogen werden.“
Ich würde das klarstellen, indem er sagt: „Wenn es kein nicht-schwacher Verweis auf ein Objekt ist“. Sobald die letzte starke Referenz entfernt wird, kann das Objekt gesammelt werden, und alle schwachen Verweise automatisch auf Null gestellt werden.
Hinweis: Dies ist nicht direkt zur Schaffung schwache Verweise verwendet, aber es gibt auch ein __strong
Attribut, aber da Objective-C Objektvariablen sind starke Referenzen standardmäßig es in der Regel nur für rohe C Zeiger auf Dinge wie structs oder Primitive, die nicht der Garbage Collector als Wurzeln behandeln verwendet wird, und wird unter Ihnen erhoben, wenn Sie sie so stark nicht erklären. (Während der Mangel an __weak
verursachen kann Zyklen und Speicherlecks halten, der Mangel an __strong
im Speicher Stampfen führen kann und wirklich seltsam und heimtückisch Fehler, die nicht-deterministisch auftreten und kann ziemlich schwierig sein, nach unten zu verfolgen.)
Andere Tipps
Einfach ändern zuweisen statt behalten, nicht mehr zirkuläre Referenzen.
@interface Block : UIImageView {
Row *yCoord;
}
@property (nonatomic,assign) Row *yCoord;
@end
Ein schwacher Verweis ist einfach eine Zuordnung (es sei denn, Sie Garbage Collection reden, die eine ganze separate Dose Würmer, aber leide nicht an Zyklen beibehalten).
Normalerweise in Cocoa, Row
würde die Block
Objekte behalten (indem man sich in der NSMutableArray einschließlich), aber Block
nicht Row
behalten, jeder würde es einfach speichern, in einer Ivar (mit einer "zuweisen" Eigenschaft).
Solange Row
vorsichtig ist, jede Block
freizugeben, bevor es freigegeben wird (dh seine dealloc
sollte die NSMutableArray freisetzen, die die Blöcke so lange freigeben wird, wie niemand sonst irgendwelche Hinweise darauf hat), dann wird alles verwenden je nach Zuordnung aufgehoben .
Sie können auch die Vorsichtsmaßnahmen ergreifen, der die Zeile Bezug von Blöcken Nullstellung, bevor Sie die entiries aus dem Array entfernt, so etwas wie:
- (void) dealloc {
for (Block* b in _blocks) {
b.row = nil;
}
[_blocks release];
[super dealloc];
}
Dabei gilt _blocks die Ivar durch die Blöcke Eigenschaft verwiesen wird.
Verwendung zuweisen schwache Verweise zu schaffen, kann in einem Multi-Thread-System unsicher sein, insbesondere dann, wenn entweder Aufgabe kann durch ein drittes Objekt zurückgehalten werden, und dann dereferenziert das andere Objekt verwendet.
Glücklicherweise ist dies oft ein Problem der Hierarchie, und das Objekt die schwache Referenz enthält, kümmert sich nur um das Objekt bezieht er sich für die genannten Objekt Lebzeiten. Dies ist die übliche Situation mit einem Superior-<->. Unterordnungsverhältnis
Ich denke, dass der Fall in Kommentar der OP-Karten dazu mit Row = Überlegene, Block = Untergeordnete.
In diesem Fall würde ich einen Griff verwenden, um die Superior von der Untergebene zu verweisen:
// Superior.h
@class Superior;
@interface SuperiorHandle : NSObject {
@private
Superior* superior_;
}
// note the deliberate avoidance of "nonatomic"
@property (readonly) Superior *superior;
@end
@interface Superior : NSObject {
@private
SuperiorHandle *handle_;
// add one or more references to Subordinate instances
}
// note the deliberate avoidance of "nonatomic"
@property (readonly) SuperiorHandle *handle;
@end
// Superior.m
#import "Superior.h"
@implementation SuperiorHandle
@synthesize
superior = superior_;
- (id)initWithSuperior:(Superior *)superior {
if ((self = [super init])) {
superior_ = superior; // weak reference
}
}
- (void)invalidate {
@synchronized (self) {
superior_ = nil;
}
}
- (Superior *)superior {
@synchronized (self) {
// retain and autorelease is required to prevent dealloc before we're ready, thanks to AndroidDev for pointing out this mistake
return [[superior_ retain] autorelease];
}
}
@end
@implementation Superior
@synthesize
handle = handle_;
- (id)init {
if ((self = [super init])) {
handle_ = [[SuperiorHandle alloc] initWithSuperior:self];
}
return self;
}
- (void)dealloc {
[handle_ invalidate];
[handle_ release];
[super dealloc];
}
@end
// Subordinate.h
@class Superior;
@class SuperiorHandle;
@interface Subordinate : NSObject {
@private
SuperiorHandle *superior_handle_;
}
@property (readonly) Superior *superior;
@end
// Subordinate.m
#import "Subordinate.h"
#import "Superior.h"
@implementation Subordinate
// no synthesize this time, superior's implementation is special
- (id)initWithSuperior:(Superior *)superior {
if ((self = [super init])) {
superior_handle_ = [superior.handle retain];
}
return self;
}
- (void)dealloc {
[superior_handle_ release];
[super dealloc];
}
- (Superior *)superior {
@synchronized (superior_handle_) {
return superior_handle_.superior;
}
}
@end
Einige Vorteile:
- Es ist sicher fädeln. Es gibt keine Möglichkeit, die schwache Referenz in nachrangigem werden ein ungültiger Zeiger enthalten haben kann. Es kann null werden, aber das ist in Ordnung.
- Es wird nur die Objekte selbst müssen über den eingebetteten schwachen Verweis kennen. Alle andere Objekte können mit nachrangigem behandeln, als ob es einen regelmäßigen Bezug auf Überlegene hat.