[Résolu] Bad access NSSet + NSValue + nonRetainedObject
Hello,
J'utilise récemment ARC et me voilà heurté à mon premier problème (Mais qui n'a surement aucun rapport avec.)
J'ajoute des instances de n'importe quel objet (tant qu'il se conforme à un protocol précis) dans un dictionnaire, qui lui ne contient qu'au maximum 4 clés avec une NSArray en valeur à chaque fois.
J'utilise NSValue +valueWithNonretainedObject: afin de stocker mes instances sans exploser le retain cycle.
- (void)__addPlayerObserver:(id<ECPlayerCoreUpdate>)anObserver forSingleUpdateType:(ECPlayerCoreUpdateType)type
{
NSMutableSet *mutObserversForUpdateType = [[NSMutableSet alloc] initWithSet:[self __playerObserversValuesForSingleUpdateType:type]];
[mutObserversForUpdateType addObject:[NSValue valueWithNonretainedObject:anObserver]];
[_observers setObject:[NSSet setWithSet:mutObserversForUpdateType]
forKey:[[self class] nameForUpdateType:type]];
}
- (void)__removePlayerObserver:(id<ECPlayerCoreUpdate>)anObserver forSingleUpdateType:(ECPlayerCoreUpdateType)type
{
NSMutableSet *mutObserversForUpdateType = [[NSMutableSet alloc] initWithSet:[self __playerObserversValuesForSingleUpdateType:type]];
[mutObserversForUpdateType removeObject:[NSValue valueWithNonretainedObject:anObserver]];
if([mutObserversForUpdateType count] > 0)
[_observers setObject:[NSSet setWithSet:mutObserversForUpdateType]
forKey:[[self class] nameForUpdateType:type]];
else
[_observers removeObjectForKey:[[self class] nameForUpdateType:type]];
}
Manque de pot, j'ai un crash qui semble aléatoire soit sur le __addPlayerObserver: ou soit le __removePlayerObserver:
Le crash se produit à la fin de la runloop donc et uniquement si un des objet qui aura été ajouté précédemment est ensuite désalloué.
J'ai un "objc_msgSend" après un dealloc sur "__NSSetI" (voir capture jointe).
J'ai du mal à cerner le problème pour le coup... Mais je me dis que ça doit venir d'une embrouille avec NSValue et son +valueWithNonretainedObject étant donné que ça ne plante qu'après qu'un objet ait été release... mais à la fin de la runLoop vu que ARC doit placer un autorelease sur mon NSMutableSet...
À noter aussi que j'appelle cette méthode dans le dealloc d'un de mes "observer" :
- (void)dealloc
{
[ECSharedPlayerCore removePlayerObserver:self];
}
(Cette méthode ne fait qu'appeler la méthode privée présentée en début de post avec plus de paramètres)
En cherchant un peu sur le net, j'ai vu qu'il était plutôt conseillé d'utiliser NSHashTable mais pour le coup j'ai du mal à voir pourquoi.. d'autant plus que c'est moins pratique que NSSet et qu'au fond c'est le même fonctionnement... (bon OK Apple conseille d'aller voir NSHashTable avant d'utiliser NSSet :P)
Solution : utiliser NSHashTable avec +weakObjectsHashTable
Réponses
Il semblerait effectivement qu'au final utiliser +valueWithNonretainedObject: ne convienne pas forcément pour ce genre de situation:
http://stackoverflow.com/questions/4692161/non-retaining-array-for-delegates
Je vais tenter avec NSHashTable
Edit: bon ben ça marche très bien avec NSHashTable et c'est tout aussi facile à utiliser que NSSet. Donc merci à moi même d'avoir continué dans mes recherches.
J'espère que ça servira à d'autres personnes :-)
Faut juste créer le CFArray avec CoreFoundation en passant la structure qu'il faut pour fournir les callbacks, et après avec le toll-free bridging tu peux l'utiliser comme n'importe quel NSArray.
Après, c'est clair que pour ton besoin NSHashTable est bien plus adapté, mais bon c'était juste pour info
Yes, j'ai vu ça dans la "best answer" du lien que j'ai posté