Ich muss schwache Verweise auf Objekte in einem NSArray speichern, um Aufbewahrungszyklen zu verhindern. Ich bin mir nicht sicher, welche Syntax ich verwenden soll. Ist das der richtige Weg?
Foo* foo1 = [[Foo alloc] init];
Foo* foo2 = [[Foo alloc] init];
__unsafe_unretained Foo* weakFoo1 = foo1;
__unsafe_unretained Foo* weakFoo2 = foo2;
NSArray* someArray = [NSArray arrayWithObjects:weakFoo1, weakFoo2, nil];
Beachten Sie, dass ich iOS 4.x unterstützen muss , also das __unsafe_unretained
statt __weak
.
EDIT (2015-02-18):
Wenn Sie echte __weak
Zeiger verwenden möchten (nicht __unsafe_unretained
), lesen Sie stattdessen diese Frage: Sammlungen von Nullen schwacher Referenzen unter ARC
objective-c
ios5
ios4
automatic-ref-counting
Emile Cormier
quelle
quelle
Antworten:
Wie Jason sagte, können Sie keine
NSArray
schwachen Referenzen erstellen. Der einfachste Weg, Emiles Vorschlag zu implementieren, ein Objekt in ein anderes Objekt zu verpacken, in dem ein schwacher Verweis darauf gespeichert ist, ist der folgende:NSValue *value = [NSValue valueWithNonretainedObject:myObj]; [array addObject:value];
Eine weitere Option: Eine Kategorie , in der
NSMutableArray
optional schwache Referenzen gespeichert werden.Beachten Sie, dass dies "unsichere, nicht zurückgehaltene" Referenzen sind, keine selbstnullenden schwachen Referenzen. Wenn das Array nach der Freigabe der Objekte noch vorhanden ist, verfügen Sie über eine Reihe von Junk-Zeigern.
quelle
NSMutableArray
ist eine schlechte Idee, da es sich um einen Klassencluster handelt und Sie eine lange Reihe von Problemen haben werden. Es ist weitaus besser, eine eigeneNSObject
Unterklasse zu erstellen , die dieselben Methoden wie hatNSMutableArray
.NSMutableArray
Klassencluster, aber es scheint, als würde eine zusätzliche Factory-Methode eingerichtet, die einen bestimmten Sicherungsspeicher verwendet, ohne jedoch eine der anderen Klassenmethoden zu beeinflussen.Die Lösungen zur Verwendung eines NSValue-Hilfsprogramms oder zum Erstellen eines Sammlungsobjekts (Array, Set, Dikt) und zum Deaktivieren seiner Retain / Release-Rückrufe sind keine 100% ausfallsicheren Lösungen für die Verwendung von ARC.
Wie verschiedene Kommentare zu diesen Vorschlägen zeigen, funktionieren solche Objektreferenzen nicht wie echte schwache Refs:
Eine "richtige" schwache Eigenschaft, wie sie von ARC unterstützt wird, weist zwei Verhaltensweisen auf:
Die oben genannten Lösungen entsprechen zwar dem Verhalten Nr. 1, weisen jedoch nicht das Verhalten Nr. 2 auf.
Um auch Verhalten Nr. 2 zu erhalten, müssen Sie Ihre eigene Hilfsklasse deklarieren. Es hat nur eine schwache Eigenschaft zum Speichern Ihrer Referenz. Anschließend fügen Sie dieses Hilfsobjekt der Sammlung hinzu.
Und noch etwas: iOS6 und OSX 10.8 bieten angeblich eine bessere Lösung:
[NSHashTable weakObjectsHashTable] [NSPointerArray weakObjectsPointerArray] [NSPointerArray pointerArrayWithOptions:]
Diese sollten Ihnen Container geben, die schwache Referenzen enthalten (beachten Sie jedoch die Kommentare von matt unten).
quelle
[NSPointer weakObjectsPointerArray]
unter iOS 6 gibt es noch keine ARC-__weak
Referenzen. Dies ist also keine bessere Lösung: Wie die von Ihnen kritisierten Lösungen entspricht sie dem Verhalten Nr. 1, jedoch nicht dem Verhalten Nr. 2. Es ist elegant und praktisch, aber es sind keine ARC-__weak
Referenzen.__weak
im vollen ARC-Sinne sind. Ich werde fragen.NSHashTable
Header schauen,+ (NSHashTable *)weakObjectsHashTable;
finden Sie einen Kommentar :// entries are not necessarily purged right away when the weak object is reclaimed
.Ich bin neu in Objective-C, nachdem ich 20 Jahre lang C ++ geschrieben habe.
Meiner Ansicht nach eignet sich Objective-C hervorragend für lose gekoppelte Nachrichten, ist jedoch für die Datenverwaltung schrecklich.
Stellen Sie sich vor, wie glücklich ich war zu entdecken, dass xcode 4.3 Objective-C ++ unterstützt!
Jetzt benenne ich alle meine .m-Dateien in .mm um (kompiliert als Objective-C ++) und verwende C ++ - Standardcontainer für die Datenverwaltung.
Somit wird das Problem "Array schwacher Zeiger" zu einem std :: -Vektor von __schwachen Objektzeigern:
#include <vector> @interface Thing : NSObject @end // declare my vector std::vector<__weak Thing*> myThings; // store a weak reference in it Thing* t = [Thing new]; myThings.push_back(t); // ... some time later ... for(auto weak : myThings) { Thing* strong = weak; // safely lock the weak pointer if (strong) { // use the locked pointer } }
Welches ist äquivalent zum c ++ Idiom:
std::vector< std::weak_ptr<CppThing> > myCppThings; std::shared_ptr<CppThing> p = std::make_shared<CppThing>(); myCppThings.push_back(p); // ... some time later ... for(auto weak : myCppThings) { auto strong = weak.lock(); // safety is enforced in c++, you can't dereference a weak_ptr if (strong) { // use the locked pointer } }
Proof of Concept (im Lichte von Tommys Bedenken hinsichtlich der Neuzuweisung von Vektoren):
main.mm:
#include <vector> #import <Foundation/Foundation.h> @interface Thing : NSObject @end @implementation Thing @end extern void foo(Thing*); int main() { // declare my vector std::vector<__weak Thing*> myThings; // store a weak reference in it while causing reallocations Thing* t = [[Thing alloc]init]; for (int i = 0 ; i < 100000 ; ++i) { myThings.push_back(t); } // ... some time later ... foo(myThings[5000]); t = nullptr; foo(myThings[5000]); } void foo(Thing*p) { NSLog(@"%@", [p className]); }
Beispiel für eine Protokollausgabe:
2016-09-21 18:11:13.150 foo2[42745:5048189] Thing 2016-09-21 18:11:13.152 foo2[42745:5048189] (null)
quelle
realloc
, bedeutet Größenänderung nicht unbedingt Bewegung. Ich denke, wir müssten das auch überprüfen.data()
oder&myThings[0]
nach dem Einfügen des ersten und vor dem Einfügen des letzten ändern? Ich entschuldige mich dafür, dass ich so unkonstruktiv bin - ich schreibe dies aus der Arbeit und mache schlanke Rechtfertigungen für die Nutzung der Zeit. Ich bin derjenige, der glaubt, dass es ein Problem geben könnte. Bitte verschwenden Sie nicht mehr Zeit, wenn Sie sich nicht sicher sind. Ich kann mich in meiner Freizeit als falsch erweisen (und verspreche, es zu versuchen).Wenn Sie keine bestimmte Bestellung benötigen, können Sie diese
NSMapTable
mit speziellen Schlüssel- / Wertoptionen verwendenquelle
Ich glaube, die beste Lösung dafür ist die Verwendung von NSHashTable oder NSMapTable. Der Schlüssel oder / und der Wert können schwach sein. Sie können hier mehr darüber lesen: http://nshipster.com/nshashtable-and-nsmaptable/
quelle
Erstellen Sie eine benutzerdefinierte Klasse mit einer schwachen Eigenschaft, wie unten angegeben, um eine schwache Selbstreferenz zu NSMutableArray hinzuzufügen.
NSMutableArray *array = [NSMutableArray new]; Step 1: create a custom class @interface DelegateRef : NSObject @property(nonatomic, weak)id delegateWeakReference; @end Step 2: create a method to add self as weak reference to NSMutableArray. But here we add the DelegateRef object -(void)addWeakRef:(id)ref { DelegateRef *delRef = [DelegateRef new]; [delRef setDelegateWeakReference:ref] [array addObject:delRef]; }
Schritt 3: Später, wenn die Eigenschaft
delegateWeakReference == nil
, kann das Objekt aus dem Array entfernt werdenDie Eigenschaft ist null, und die Referenzen werden zum richtigen Zeitpunkt unabhängig von diesen Array-Referenzen freigegeben
quelle
Die einfachste Lösung:
NSMutableArray *array = (__bridge_transfer NSMutableArray *)CFArrayCreateMutable(nil, 0, nil); NSMutableDictionary *dictionary = (__bridge_transfer NSMutableDictionary *)CFDictionaryCreateMutable(nil, 0, nil, nil); NSMutableSet *set = (__bridge_transfer NSMutableSet *)CFSetCreateMutable(nil, 0, nil);
Hinweis: Und dies funktioniert auch unter iOS 4.x.
quelle
CFMutableArrayRef CFArrayCreateMutable ( CFAllocatorRef allocator, CFIndex capacity, const CFArrayCallBacks *callBacks );
Weitere Informationen finden Sie in der Dokumentation.__unsafe_unretained
und dies liefert richtig__unsafe_unretained
; Es wäre schön, wenn Sie dem Missbrauch des Begriffsweak
durch den ursprünglichen Autor nicht folgen würden .Nein, das stimmt nicht. Das sind eigentlich keine schwachen Referenzen. Sie können derzeit keine schwachen Referenzen in einem Array speichern. Sie müssen über ein veränderbares Array verfügen und die Referenzen entfernen, wenn Sie damit fertig sind, oder das gesamte Array entfernen, wenn Sie damit fertig sind, oder Ihre eigene Datenstruktur rollen, die dies unterstützt.
Hoffentlich ist dies etwas, das sie in naher Zukunft ansprechen werden (eine schwache Version von
NSArray
).quelle
__unsafe_unretained
Zeiger auf einFoo
anderes Objekt umbringe, das von der beibehalten werden kannNSArray
?__unsafe_unretained
. Das verhindert einen Aufbewahrungszyklus, aber Sie könnten sehr leicht auf Müll zugreifen.shared_ptr
. :-)__unsafe_unretained
, kann ich ein Makro verwenden, das je nach Basis-SDK-Version entweder__unsafe_unretained
oder erweitert wird__weak
. Auf diese Weise kann ich während der Entwicklung auf meinem iOS 5-Gerät Probleme mit baumelnden Zeigern erkennen.Ich hatte gerade das gleiche Problem und stellte fest, dass meine Vor-ARC-Lösung nach der Konvertierung mit ARC wie geplant funktioniert.
// function allocates mutable set which doesn't retain references. NSMutableSet* AllocNotRetainedMutableSet() { CFMutableSetRef setRef = NULL; CFSetCallBacks notRetainedCallbacks = kCFTypeSetCallBacks; notRetainedCallbacks.retain = NULL; notRetainedCallbacks.release = NULL; setRef = CFSetCreateMutable(kCFAllocatorDefault, 0, ¬RetainedCallbacks); return (__bridge NSMutableSet *)setRef; } // test object for debug deallocation @interface TestObj : NSObject @end @implementation TestObj - (id)init { self = [super init]; NSLog(@"%@ constructed", self); return self; } - (void)dealloc { NSLog(@"%@ deallocated", self); } @end @interface MainViewController () { NSMutableSet *weakedSet; NSMutableSet *usualSet; } @end @implementation MainViewController - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; if (self) { // Custom initialization weakedSet = AllocNotRetainedMutableSet(); usualSet = [NSMutableSet new]; } return self; } - (IBAction)addObject:(id)sender { TestObj *obj = [TestObj new]; [weakedSet addObject:obj]; // store unsafe unretained ref [usualSet addObject:obj]; // store strong ref NSLog(@"%@ addet to set", obj); obj = nil; if ([usualSet count] == 3) { [usualSet removeAllObjects]; // deallocate all objects and get old fashioned crash, as it was required. [weakedSet enumerateObjectsUsingBlock:^(TestObj *invalidObj, BOOL *stop) { NSLog(@"%@ must crash here", invalidObj); }]; } } @end
Ausgabe:
Überprüft mit iOS-Versionen 4.3, 5.1, 6.2. Hoffe, es wird jemandem nützlich sein.
quelle
Wenn Sie schwache Referenzen auf Null setzen müssen , finden Sie in dieser Antwort Code, den Sie für eine Wrapper-Klasse verwenden können.
Andere Antworten auf diese Frage schlagen einen blockbasierten Wrapper und Möglichkeiten zum automatischen Entfernen von Elementen mit Nullen aus der Sammlung vor.
quelle
Wenn Sie dieses Verhalten häufig verwenden, wird es Ihrer eigenen NSMutableArray-Klasse (Unterklasse von NSMutableArray) angezeigt, wodurch die Anzahl der Aufbewahrungen nicht erhöht wird.
Sie sollten so etwas haben:
-(void)addObject:(NSObject *)object { [self.collection addObject:[NSValue valueWithNonretainedObject:object]]; } -(NSObject*) getObject:(NSUInteger)index { NSValue *value = [self.collection objectAtIndex:index]; if (value.nonretainedObjectValue != nil) { return value.nonretainedObjectValue; } //it's nice to clean the array if the referenced object was deallocated [self.collection removeObjectAtIndex:index]; return nil; }
quelle
Ich denke, eine elegante Lösung ist das, was Herr Erik Ralston in seinem Github-Repository vorschlägt
https://gist.github.com/eralston/8010285
Dies sind die wesentlichen Schritte:
Erstellen Sie eine Kategorie für NSArray und NSMutableArray
Erstellen Sie in der Implementierung eine Convenience-Klasse mit einer schwachen Eigenschaft. Ihre Kategorie weist die Objekte dieser schwachen Eigenschaft zu.
.h
#import <Foundation/Foundation.h> @interface NSArray(WeakArray) - (__weak id)weakObjectForIndex:(NSUInteger)index; -(id<NSFastEnumeration>)weakObjectsEnumerator; @end @interface NSMutableArray (FRSWeakArray) -(void)addWeakObject:(id)object; -(void)removeWeakObject:(id)object; -(void)cleanWeakObjects; @end
.m
#import "NSArray+WeakArray.h" @interface WAArrayWeakPointer : NSObject @property (nonatomic, weak) NSObject *object; @end @implementation WAArrayWeakPointer @end @implementation NSArray (WeakArray) -(__weak id)weakObjectForIndex:(NSUInteger)index { WAArrayWeakPointer *ptr = [self objectAtIndex:index]; return ptr.object; } -(WAArrayWeakPointer *)weakPointerForObject:(id)object { for (WAArrayWeakPointer *ptr in self) { if(ptr) { if(ptr.object == object) { return ptr; } } } return nil; } -(id<NSFastEnumeration>)weakObjectsEnumerator { NSMutableArray *enumerator = [[NSMutableArray alloc] init]; for (WAArrayWeakPointer *ptr in self) { if(ptr && ptr.object) { [enumerator addObject:ptr.object]; } } return enumerator; } @end @implementation NSMutableArray (FRSWeakArray) -(void)addWeakObject:(id)object { if(!object) return; WAArrayWeakPointer *ptr = [[WAArrayWeakPointer alloc] init]; ptr.object = object; [self addObject:ptr]; [self cleanWeakObjects]; } -(void)removeWeakObject:(id)object { if(!object) return; WAArrayWeakPointer *ptr = [self weakPointerForObject:object]; if(ptr) { [self removeObject:ptr]; [self cleanWeakObjects]; } } -(void)cleanWeakObjects { NSMutableArray *toBeRemoved = [[NSMutableArray alloc] init]; for (WAArrayWeakPointer *ptr in self) { if(ptr && !ptr.object) { [toBeRemoved addObject:ptr]; } } for(WAArrayWeakPointer *ptr in toBeRemoved) { [self removeObject:ptr]; } } @end
quelle