[Résolu] Bad access NSSet + NSValue + nonRetainedObject

août 2013 modifié dans Objective-C, Swift, C, C++ #1

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

  • août 2013 modifié #2

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


  • AliGatorAliGator Membre, Modérateur
    août 2013 modifié #3
    Sinon pour info avec CoreFoundation tu peux créer des CFArray et CFDictionary avec des politiques de retain/release différentes des politiques standard. Par exemple faire un NSCFArray qui ne retain pas les objets qu'il stocke.
    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 ;)


  • Sinon pour info avec CoreFoundation tu peux créer des CFArray et CFDictionary avec des politiques de retain/release différentes des politiques standard. Par exemple faire un NSCFArray qui ne retain pas les objets qu'il stocke.

    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é :D

Connectez-vous ou Inscrivez-vous pour répondre.