NSArray von schwachen Verweisen (__unsafe_unretained) auf Objekte unter ARC


70

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_unretainedstatt __weak.


EDIT (2015-02-18):

Wenn Sie echte __weakZeiger verwenden möchten (nicht __unsafe_unretained), lesen Sie stattdessen diese Frage: Sammlungen von Nullen schwacher Referenzen unter ARC


13
"Ich habe Mitleid mit dem schwachen Foo!"
Emile Cormier

4
Ich würde vorschlagen, das Framework nicht zu bekämpfen und NSPointerArray mit NSPointerFunctionsWeakMemory NSPointerFunctionOption
Leviathan

1
@leviathan: Diese Frage wurde gestellt, bevor iOS 6 herauskam.
Emile Cormier

Ich habe dies als Wörterbuch erstellt, in dem Objekte so gespeichert werden, dass schwache Referenzen effektiv auf Null gesetzt werden. Es könnte geändert (und bereinigt) werden, um Ihren Zwecken zu dienen.
Ethan Reesor

Antworten:


75

Wie Jason sagte, können Sie keine NSArrayschwachen 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 NSMutableArrayoptional 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.


Gern geschehen! Überprüfen Sie auch meine aktualisierte Antwort für eine andere Option.
Yuji

Ich denke, eine Kategorie NSMutableArrayist eine schlechte Idee, da es sich um einen Klassencluster handelt und Sie eine lange Reihe von Problemen haben werden. Es ist weitaus besser, eine eigene NSObjectUnterklasse zu erstellen , die dieselben Methoden wie hat NSMutableArray.
Abhi Beckert

1
Haben Sie sich die tatsächliche Lösung angesehen, mit der ich verlinkt habe? Ich bin kein Experte für NSMutableArrayKlassencluster, 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.
Yuji

16
HINWEIS: Dies sind keine schwachen Referenzen, die sich selbst auf Null setzen. Sie werden einfach nicht zurückgehalten.
mxcl

Ja, wie @mxcl sagte, diese sind unsicher, nicht zurückgehalten, nicht schwach. Es stürzt also ab, wenn Sie versuchen, den Wert nach der Freigabe zu entpacken. Verwenden Sie stattdessen NSMapTable und NSHashTable.
Mikhail Larionov

59

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:

  1. Hält keinen starken Verweis auf das Zielobjekt. Das heißt, wenn auf das Objekt keine starken Verweise verweisen, wird das Objekt freigegeben.
  2. Wenn die Zuordnung des ref'd-Objekts aufgehoben wird, wird die schwache Referenz gleich Null.

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).


[NSPointer weakObjectsPointerArray]unter iOS 6 gibt es noch keine ARC- __weakReferenzen. 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- __weakReferenzen.
Matt

@matt Wie haben Sie festgestellt, dass schwachObjectsPointerArray keine __schwachen Refs zurückgibt? Haben Sie es im Code getestet oder beziehen Sie sich auf die Dokumentation "NSPointerArray-Klassenreferenz" mit der Aufschrift "Wichtig: NSPointerArray unterstützt keine schwachen Referenzen unter Automatic Reference Counting (ARC)."? Ich hatte mich gefragt, ob das nicht nur ein Überbleibsel aus der Zeit vor 10.8 war und ein Dokumentationsfehler ist.
Thomas Tempelmann

1
Nun, ich hätte nie gedacht, dass eine große Warnung in einer Schachtel "nur ein Überbleibsel" sein würde. Die Referenzen sind eindeutig schwach und werden eindeutig NULL, wenn das Objekt freigegeben wurde, aber ich weiß nicht, wie ich testen soll, ob sie __weakim vollen ARC-Sinne sind. Ich werde fragen.
Matt

2
Wenn sie NULL werden, wenn ihr Objekt freigegeben wird, passen sie zu meinem Verhalten Nr. 2, und dann ist alles gut. Vor ARC war ein schwacher Ref nur ein Zeiger (Typ-ID), der nach der Freigabe des referenzierten Objekts baumelte. Der schwache Ref von ARC ist intelligenter, da er dann NULL wird, was die Verwendung sicherer macht. Wenn dies das Verhalten ist, das Sie sehen, ist das Warnfeld in den Dokumenten in der Tat ein Fehler. Ich hatte auch ein Feedback dazu gesendet und sie gebeten, es noch einmal zu überprüfen. Leider geben sie (Apple) kein Feedback :(
Thomas Tempelmann

2
Übrigens, wenn Sie in den NSHashTableHeader schauen, + (NSHashTable *)weakObjectsHashTable;finden Sie einen Kommentar : // entries are not necessarily purged right away when the weak object is reclaimed.
Krzysztof Przygoda

27

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)

14
Ja, aber dies ist keine echte objektive C-Lösung. du magst es einfach, weil du ein C ++ - Typ bist.
Aran Mulholland

10
Aran, ich bin ein Typ, der versucht, die besten verfügbaren Werkzeuge für den Job zu verwenden. Objective-C verfügt nicht über gute Datenverarbeitungsfunktionen, da es im Wesentlichen C mit einigen Messaging-Hacks und einer umfangreichen unterstützenden Bibliothek ist. Es ist eine anachronistische und veraltete Sprache (daher versucht Apple, sie durch SWIFT zu ersetzen). Da wir die Möglichkeit haben, den hervorragenden C ++ - Compiler zu verwenden, der mit xcode geliefert wird, warum nicht?
Richard Hodges

2
Dies ist nicht Objective-c, sondern Objective-C ist nicht C. Es wird vom Compiler nativ unterstützt und verfügt über bessere Speicherverwaltungsfunktionen
André Fratelli

2
Ich verpacke alle Objective-C-Objekte in leichte C ++ - Wrapper. Es sorgt für sicheren Code und vermeidet Apples neueste Idiotie - Swift.
Richard Hodges

1
Ich bin der Meinung, dass dies nur die wahrscheinliche Sicherheit beweist - unter der Annahme, dass letztendlich die gleiche Realität zugrunde liegt wie 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).
Tommy

13

Wenn Sie keine bestimmte Bestellung benötigen, können Sie diese NSMapTablemit speziellen Schlüssel- / Wertoptionen verwenden

NSPointerFunctionsWeakMemory

Verwendet schwache Lese- und Schreibbarrieren, die für ARC oder GC geeignet sind. Bei Verwendung von NSPointerFunctionsWeakMemory werden Objektreferenzen in der letzten Version auf NULL gesetzt.


Dies ist die richtige Antwort, wenn Sie
satzartiges


4

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 werden

Die Eigenschaft ist null, und die Referenzen werden zum richtigen Zeitpunkt unabhängig von diesen Array-Referenzen freigegeben


Hallo MistyD, bitte lass mich wissen, warum du diesen Beitrag bearbeitet hast. Ich habe keine Änderung gesehen. Bitte bearbeiten Sie meinen Beitrag nicht
Harish Kumar Kailas

Sie können auf die Zeile "bearbeitet" klicken, um zu sehen, was geändert wurde. Wenn ich mir die Bearbeitung ansehe, sehe ich, dass Misty Ihren Codeblöcken Formatierungen hinzugefügt hat, damit sie als Code angezeigt werden. Keiner Ihrer Inhalte wurde geändert. Dies ist eine absolut gültige Bearbeitung für jemanden, und Sie sollten nicht erwarten, dass andere Ihre Beiträge nicht bearbeiten.
John Stephen

@ HarishKumarKailas Sie haben Ihren Code in einen Codeblock formatiert. Sie haben ihn als Klartext veröffentlicht, von dem abgeraten wird.
Albert Renshaw

4

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.


2
Es ist nicht klar, warum eines dieser Objekte schwache Objekte enthält.
Graham Perks

Null-Allokator. Schauen Sie sich die Deklaration dieser Methoden an. CFMutableArrayRef CFArrayCreateMutable ( CFAllocatorRef allocator, CFIndex capacity, const CFArrayCallBacks *callBacks );Weitere Informationen finden Sie in der Dokumentation.
ArtFeel

2
Die Frage fragt __unsafe_unretainedund dies liefert richtig __unsafe_unretained; Es wäre schön, wenn Sie dem Missbrauch des Begriffs weakdurch den ursprünglichen Autor nicht folgen würden .
Tommy

3

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).


Was ist, wenn ich einen __unsafe_unretainedZeiger auf ein Fooanderes Objekt umbringe, das von der beibehalten werden kann NSArray?
Emile Cormier

@EmileCormier Sie könnten das tun, aber Sie sollten es zu einer schwachen Eigenschaft machen, nicht __unsafe_unretained. Das verhindert einen Aufbewahrungszyklus, aber Sie könnten sehr leicht auf Müll zugreifen.
Jason Coco

Ich wünschte, ich könnte eine schwache Eigenschaft verwenden, aber ich kann den iOS 4.x-Markt für meine App nicht ignorieren. Mein Hintergrund ist C / C ++, daher bin ich es gewohnt, in schlechten alten Zeiten mit dem Feuer zu spielen shared_ptr. :-)
Emile Cormier

Je nachdem, wann Ihre App herauskommt, möchten Sie möglicherweise iOS 4 ignorieren! :) Wenn es dem gleichen Trend wie iOS 4 folgt, sollte iOS 5 6 Monate nach dem Start in etwa 90% der Geräte verwendet werden, und diese Marke ist im April, sodass sich die zusätzliche Arbeit zur Unterstützung von iOS 4 möglicherweise nicht für das bezahlt Zeitaufwand für das Codieren / Debuggen.
Fernando Madruga

2
Hatte gerade eine Idee ... Anstatt zu verwenden __unsafe_unretained, kann ich ein Makro verwenden, das je nach Basis-SDK-Version entweder __unsafe_unretainedoder erweitert wird __weak. Auf diese Weise kann ich während der Entwicklung auf meinem iOS 5-Gerät Probleme mit baumelnden Zeigern erkennen.
Emile Cormier

2

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,
    &notRetainedCallbacks);
    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:

2013-06-30 00: 59: 10.266 not_retained_collection_test [28997: 907] erstellt 2013-06-30 00: 59: 10.267 not_retained_collection_test [28997: 907] Addet zum Setzen von 2013-06-30 00: 59: 10.581 not_retained_collection_test [28997: 907] erstellt 2013-06-30 00: 59: 10.582 not_retained_collection_test [28997: 907] addet zum Setzen von 2013-06-30 00: 59: 10.881 not_retained_collection_test [28997: 907] erstellt 2013-06-30 00: 59: 10.882 not_retained_collection_test [28997: 907] Addet zum Festlegen des 30.06.2013 00: 59: 10.883 not_retained_collection_test [28997: 907] freigegeben 2013-06-30 00: 59: 10.883 not_retained_collection_test [28997: 907] freigegeben 2013-06-30 00:59 freigegeben : 10.884 not_retained_collection_test [28997: 907] freigegeben 2013-06-30 00: 59: 10.885 not_retained_collection_test [28997: 907] * - [TestObj responsondsToSelector:]: Nachricht an freigegebene Instanz 0x1f03c8c0 gesendet

Überprüft mit iOS-Versionen 4.3, 5.1, 6.2. Hoffe, es wird jemandem nützlich sein.


1

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.


1

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;
}

-2

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

Während dieser Link die Frage beantworten kann, ist es besser, die wesentlichen Teile der Antwort hier aufzunehmen und den Link als Referenz bereitzustellen. Nur-Link-Antworten können ungültig werden, wenn sich die verknüpfte Seite ändert. - Aus dem Rückblick
Nigel Ren

Code zur Antwort hinzugefügt. Können Sie jetzt erklären, was mit der Lösung nicht stimmt, um abgelehnt zu werden?
Enrico Cupellini

Es wurde möglicherweise wegen fehlenden Codes herabgestimmt, aber leider hat sich derjenige, der Sie herabgestimmt hat, nicht die Mühe gemacht, etwas zu sagen (was ich auch ärgerlich finde).
Nigel Ren
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.