70 votes

NSArray de références faibles (__unsafe_unretained) à des objets sous ARC

J'ai besoin de stocker des références faibles à des objets dans un NSArray, afin d'éviter les cycles de rétention. Je ne suis pas sûr de la syntaxe à utiliser. Est-ce la bonne méthode ?

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

Notez que j'ai besoin de supporter iOS 4.x Ainsi, le __unsafe_unretained au lieu de __weak .


EDITAR (2015-02-18) :

Pour ceux qui veulent utiliser le vrai __weak pointeurs (pas __unsafe_unretained ), veuillez plutôt répondre à cette question : Collections de références faibles de mise à zéro sous ARC

13 votes

"Je plains le faible foo !"

4 votes

Je suggère de ne pas lutter contre le cadre et d'utiliser NSPointerArray avec l'option NSPointerFunctionsWeakMemory NSPointerFunctionOption.

1 votes

@leviathan : Cette question a été posée avant la sortie d'iOS 6.

75voto

yuji Points 11531

Comme Jason l'a dit, vous ne pouvez pas faire NSArray stocker des références faibles. La manière la plus simple d'implémenter la suggestion d'Emile d'envelopper un objet dans un autre objet qui stocke une référence faible à celui-ci est la suivante :

NSValue *value = [NSValue valueWithNonretainedObject:myObj];
[array addObject:value];

Autre option : un catégorie qui fait NSMutableArray stocker éventuellement des références faibles.

Notez qu'il s'agit de références "unsafe unretained", et non de références faibles à remise à zéro automatique. Si le tableau est toujours là après que les objets aient été désalloués, vous aurez un tas de pointeurs inutiles.

0 votes

Mon plaisir ! Consultez également ma réponse mise à jour pour une autre option.

0 votes

Je pense qu'une catégorie sur les NSMutableArray est une mauvaise idée, car il s'agit d'un groupe de classes et vous vous engagerez dans une longue série de problèmes. Il est de loin préférable de créer votre propre NSObject qui possède toutes les mêmes méthodes que la classe NSMutableArray .

1 votes

Avez-vous regardé la solution que j'ai mise en lien ? Je ne suis pas un expert en NSMutableArray et les clusters de classe, mais il semble qu'il mette en place une méthode d'usine supplémentaire qui utilise un backing store spécifique, alors qu'il n'affecte aucune des autres méthodes de la classe.

61voto

Thomas Tempelmann Points 2849

Les solutions pour utiliser un Valeur NSValue ou pour créer un objet collection (array, set, dict) et désactiver ses callbacks Retain/Release sont toutes deux des solutions qui ne sont pas sûres à 100 % en ce qui concerne l'utilisation de l'ARC.

Comme le soulignent divers commentaires sur ces suggestions, ces références d'objets ne fonctionneront pas comme de véritables références faibles :

Une propriété faible "correcte", telle que soutenue par l'ARC, a deux comportements :

  1. Il n'y a pas de référence forte à l'objet cible. Cela signifie que si l'objet n'a aucune référence forte pointant vers lui, l'objet sera désalloué.
  2. Si l'objet référencé est désalloué, la référence faible deviendra nulle.

Maintenant, bien que les solutions ci-dessus soient conformes au comportement n° 1, elles ne répondent pas au comportement n° 2.

Pour obtenir également le comportement n° 2, vous devez déclarer votre propre classe d'aide. Elle n'a qu'une seule propriété faible pour contenir votre référence. Vous ajoutez ensuite cet objet d'aide à la collection.

Oh, et une dernière chose : iOS6 et OSX 10.8 offrent soi-disant une meilleure solution :

[NSHashTable weakObjectsHashTable]
[NSPointerArray weakObjectsPointerArray]
[NSPointerArray pointerArrayWithOptions:]

Cela devrait vous donner des conteneurs qui contiennent des références faibles (mais notez les commentaires de Matt ci-dessous).

0 votes

[NSPointer weakObjectsPointerArray] sur iOS 6 est toujours no ARC __weak des références. Il s'agit donc de no une meilleure solution : comme les solutions que vous critiquez, elle respecte le comportement n° 1 mais pas le n° 2. Elle est élégante et pratique, mais ce n'est pas l'ARC. __weak des références.

0 votes

@matt Comment avez-vous trouvé que weakObjectsPointerArray ne retourne pas __weak refs ? L'avez-vous testé dans le code ou vous référez-vous à la documentation "NSPointerArray Class Reference" qui dit "Important : NSPointerArray does not support weak references under Automatic Reference Counting (ARC)" ? Je me suis demandé si ce n'était pas un reliquat de l'époque pré-10.8 et une erreur de documentation.

1 votes

Eh bien, je n'aurais jamais imaginé qu'un énorme avertissement dans une boîte puisse être "juste un reste". Les références sont clairement faibles, et elles sont clairement faites NULL lorsque l'objet a été libéré, mais je ne sais pas comment tester qu'elles le sont. __weak au sens plein de l'ARC. Je vous le demande.

29voto

Richard Hodges Points 1972

Je suis novice en Objective-C, après 20 ans d'écriture en C++.

À mon avis, objective-C est excellent pour la messagerie à couplage lâche, mais horrible pour la gestion des données.

Imaginez ma joie de découvrir que xcode 4.3 supporte objective-c++ !

Maintenant, je renomme tous mes fichiers .m en .mm (compilés comme objective-c++) et j'utilise les conteneurs standard c++ pour la gestion des données.

Ainsi, le problème du "tableau de pointeurs faibles" devient un std::vector de pointeurs d'objets __faibles :

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

Ce qui est équivalent à l'idiome c++ :

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

Preuve de concept (à la lumière des préoccupations de Tommy sur la réallocation des vecteurs) :

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

exemple de sortie de journal :

2016-09-21 18:11:13.150 foo2[42745:5048189] Thing
2016-09-21 18:11:13.152 foo2[42745:5048189] (null)

14 votes

Oui, mais ce n'est pas une vraie solution objective en C. Vous l'aimez juste parce que vous êtes un gars de C++.

11 votes

Aran, je suis un homme qui cherche à utiliser les meilleurs outils disponibles pour le travail. objective-c n'a pas de bonnes capacités de traitement des données, étant essentiellement C avec quelques bidouillages de messagerie et une bibliothèque de soutien massive. C'est un langage anachronique et obsolète (d'où la volonté d'Apple de le remplacer par SWIFT). Puisque nous avons la possibilité d'utiliser l'excellent compilateur c++ livré avec xcode, pourquoi ne pas le faire ?

2 votes

Il ne s'agit pas d'Objective-c, tout comme Objective-C n'est pas C. Il est pris en charge en mode natif par le compilateur et dispose de meilleures capacités de gestion de la mémoire.

13voto

Erik Aigner Points 7182

Si vous n'avez pas besoin d'un ordre spécifique, vous pourriez utiliser NSMapTable avec des options clés/valeurs spéciales

NSPointerFunctionsWeakMemory

Utilise des barrières faibles de lecture et d'écriture appropriées pour ARC ou GC. L'utilisation de références d'objets NSPointerFunctionsWeakMemory se transformera en NULL à la dernière version.

0 votes

C'est la bonne réponse, si vous pouvez tolérer un comportement de type "set" (au lieu de "array").

11voto

Yaniv De Ridder Points 433

Je pense que la meilleure solution pour cela est d'utiliser NSHashTable ou NSMapTable. La clé ou/et la valeur peuvent être faibles. Vous pouvez en savoir plus à ce sujet ici : http://nshipster.com/nshashtable-and-nsmaptable/

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X