Objective-C crash sur le détruire helper bloc

j'ai une application iOS qui s'effondre sur des appels comme __destroy_helper_block_253 et __destroy_helper_block_278 et je ne suis pas vraiment sûr de ce que" destroy_helper_block " est le référencement ou ce que le nombre après qu'il est censé pointer.

quelqu'un avez des conseils pour pouvoir savoir où exactement ces accidents pourraient être présentes?

voici un exemple de traceback (notez que les lignes avec __destroy_helper_block ne font référence qu'au fichier qu'il contient et rien d'autre, alors que normalement le numéro de ligne serait également inclus).

Thread : Crashed: com.apple.root.default-priority
0  libdispatch.dylib              0x000000018fe0eb2c _dispatch_semaphore_dispose + 60
1  libdispatch.dylib              0x000000018fe0e928 _dispatch_dispose + 56
2  libdispatch.dylib              0x000000018fe0e928 _dispatch_dispose + 56
3  libdispatch.dylib              0x000000018fe0c10c -[OS_dispatch_object _xref_dispose] + 60
4  Example App                    0x00000001000fe5a4 __destroy_helper_block_278 (TSExampleApp.m)
5  libsystem_blocks.dylib         0x000000018fe53908 _Block_release + 256
6  Example App                    0x00000001000fda18 __destroy_helper_block_253 (TSExampleApp.m)
7  libsystem_blocks.dylib         0x000000018fe53908 _Block_release + 256
8  libdispatch.dylib              0x000000018fe0bfd4 _dispatch_client_callout + 16
9  libdispatch.dylib              0x000000018fe132b8 _dispatch_root_queue_drain + 556
10 libdispatch.dylib              0x000000018fe134fc _dispatch_worker_thread2 + 76
11 libsystem_pthread.dylib        0x000000018ffa16bc _pthread_wqthread + 356

Edit 1: Voici un exemple d'un des blocs définis dans le fichier où le crash se produit (avec le code propre à l'application éliminé).

- (void)doSomethingWithCompletion:(void (^)())completion {
    void (^ExampleBlock)(NSString *) = ^{
        NSNotification *notification = [NSNotification notificationWithName:kExampleNotificationName object:nil userInfo:nil];
        [[NSNotificationCenter defaultCenter] postNotification:notification];

        if (completion) {
            completion();
        }
    };

    // Async network call that calls ExampleBlock on either success or failure below...
}

il y a beaucoup d'autres blocs dans le fichier, mais la plupart d'entre eux sont fournis comme arguments aux méthodes au lieu d'être définis d'abord et ensuite référencés plus tard.

Edit 2: ajouté plus de contexte à la fonction ci-dessus.

24
demandé sur Dan Loewenherz 2014-05-10 00:07:30

7 réponses

chaque image de la trace de la pile devrait vous donner un indice sur ce que libDispatch fait pour causer le crash. Travailler à notre façon à partir du bas:

11 libsystem_pthread.dylib        0x000000018ffa16bc _pthread_wqthread
10 libdispatch.dylib              0x000000018fe134fc _dispatch_worker_thread2 + 76

ces deux fonctions font tourner un fil ouvrier et le font fonctionner. Dans le processus, il met également en place un pool autorelease pour le fil.

9  libdispatch.dylib              0x000000018fe132b8 _dispatch_root_queue_drain + 556

cette fonction signale le début du processus de destruction de la file d'attente. Le bassin d'autorelase spécifique au fil est drainé, et dans le traiter toutes les variables référencées par cette file d'attente sont libérés. Comme il s'agit de libDispatch, cela signifie que l'objet mach sous-jacent et le bloc de travail que vous avez soumis doivent être supprimés...

7  libsystem_blocks.dylib         0x000000018fe53908 _Block_release + 256
6  Example App                    0x00000001000fda18 __destroy_helper_block_253 (TSExampleApp.m)
5  libsystem_blocks.dylib         0x000000018fe53908 _Block_release + 25
4  Example App                    0x00000001000fe5a4 __destroy_helper_block_278 (TSExampleApp.m)

qui est précisément ce qui se passe ici. Le numéro 7 est le bloc extérieur et parce qu'il contient un objet non trivial à détruire (encore un autre bloc), le compilateur a généré un destructeur ( __destroy_helper_block_253 ) pour se débarrasser de ce bloc intérieur aussi. En appliquant la même logique, nous pouvons en déduire que le bloc intérieur a encore un peu de destruction non triviale à faire.

3  libdispatch.dylib              0x000000018fe0c10c -[OS_dispatch_object _xref_dispose] + 60
2  libdispatch.dylib              0x000000018fe0e928 _dispatch_dispose + 56
1  libdispatch.dylib              0x000000018fe0e928 _dispatch_dispose + 56

Ces lignes sont la cause de tous vos problèmes. Pour une raison quelconque, soit vous avez capturé la file d'attente sur laquelle vous rappelez, soit vous avez capturé un objet qui contient une référence à une file d'attente faiblement telle que quand il va dans le sens du dinosaure, il prend sa file d'attente avec elle. Cela fait en sorte que libDispatch suppose que la file d'attente est terminée et qu'elle continue à désallouer jusqu'à ce que il atteint l'élimination spécifique au sémaphore 1519100920"

0  libdispatch.dylib              0x000000018fe0eb2c _dispatch_semaphore_dispose + 60

sans sémaphore à libérer, mach se plaindra assez pour ne pas retourner KERN_SUCCESS sur destruction sémaphore, ce qui est une erreur fatale dans libDispatch. En fait, il sera abort() dans un tel cas-eh bien, techniquement __builtin_trap() , mais ils accomplissent le même objectif. Parce qu'il n'y a pas de débogueur attaché, descend ton application.

cela soulève donc la question: comment arranger cela? Bien, tout d'abord, vous devez trouver quoi, si quelque chose se réfère à un objet dispatch. Vous avez mentionné que vous faisiez du réseautage asynchrone, donc ce serait l'endroit de vérifier d'abord. Si l'un de ces objets se trouve à contenir une file d'attente ou un sémaphore, ou renvoie un objet qui le fait, et que vous ne le capturez pas fortement dans l'un de ces blocs, c'est précisément ce qui se produit lorsque le bloc passe hors de portée avec l'objet.

32
répondu CodaFi 2014-05-17 21:04:22

Il n'y a pas beaucoup d'aller ici, mais je soupçonne que le bloc n'est jamais déplacé vers le tas. Les blocs sont créés sur la pile par défaut. Le compilateur peut souvent trouver le moment de les déplacer en tas, mais la façon dont vous passez celui-ci d'un bloc à l'autre ne le fait probablement jamais.

j'ajouterais un completionCopy = [completion copy] pour le forcer sur le tas. Puis travailler avec completionCopy . Voir réponse de bbum concernant le stockage des blocs dans dictionnaire. Avec ARC, vous n'avez plus besoin d'appeler Block_copy() et Block_release() , mais je pense que vous voulez toujours appeler -copy ici.

2
répondu Rob Napier 2017-05-23 11:47:26

hypothèse:

  1. doSomethingWithCompletion: crée ExampleBlock.
  2. vous démarrez une opération de réseau asynchrone.
  3. doSomethingWithCompletion: les retours, et les ExampleBlock est sorti.
  4. le fonctionnement asynchrone du réseau se termine, et appelle ExampleBlock .

dans ce cas, le pointeur vers le bloc serait déréférencé après qu'il ait été désalloué. (Peut-être est-ce intermittent si l'on se base sur le fait que le bassin d'autorelease s'est drainé ou si d'autres zones de mémoire à proximité ont été libérées.)

3 solutions possibles:

1. Stocker le bloc dans une propriété

stocker le bloc dans une propriété:

@property (nonatomic, copy) returnType (^exampleBlock)(parameterTypes);

puis en code,

self.exampleBlock = …

Un problème avec cette approche est que vous ne pouvez jamais avoir un exampleBlock .

2. Stocker le bloc dans un tableau

Pour contourner ce problème, vous pouvez stocker les blocs dans une collection (comme NSMutableArray ):

@property (nonatomic, strong) NSMutableArray *blockArray;

puis en code:

self.blockArray = [NSMutableArray array];

// Later on…
[self.blockArray addObject:exampleBlock];

vous pouvez enlever le bloc du tableau quand il est correct de le désallouer.

3. Travailler autour du problème de stockage en passant simplement le bloc autour de

au lieu de gérer le stockage et la destruction de vos blocs, remaniez votre code de façon à ce que exampleBlock soit transmis entre les différentes méthodes jusqu'à la fin de votre opération.

alternativement, vous pouvez utiliser NSBlockOperation pour le code asynchrone, et définir son completionBlock pour la réponse-code fini, et l'ajouter à une Nsopérationqueue.

1
répondu Aaron Brager 2014-05-16 20:23:32

je pense que le completion est libéré dans votre appel async qui pourrait être à l'origine du crash.

1
répondu Anand 2014-05-17 13:06:15

je soupçonne, le problème n'est pas dans votre code mais ailleurs.

un problème possible est celui-ci:

IFF il y a des objets UIKit qui sont capturés dans le bloc completion vous avez probablement un bug subtil quand le bloc est exécuté sur un thread non-principal et ce bloc garde le dernier forte référence à ces objets UIKit:

quand le bloc completion se termine, il est Bloc obtenir désalloué et avec cela, toutes les variables importées sont " détruites "ce qui signifie qu'en cas de pointeurs readable, elles reçoivent un message release . Si c'était la dernière référence forte, l'objet capturé est désallocalisé ce qui se produit dans un thread non-principal - et cela peut être fatal pour les objets UIKit.

1
répondu CouchDeveloper 2014-05-17 15:04:44

Je ne vois rien de mal avec le code posté et je pense que le bug est ailleurs.

aussi blocs de nidification semble inutile et complique la gestion de la mémoire et rend probablement plus difficile de trouver la cause de l'accident.

pourquoi ne pas commencer par déplacer le code de votre ExampleBlock directement dans le bloc completion ?

0
répondu Rivera 2014-05-16 02:08:36

qu'en est-il de cette solution: si vous appellerez plus tard un bloc qui n'est pas dans la portée courante, alors vous devriez appeler une copie dessus pour déplacer ce bloc dans le tas à partir de la pile

- (void)doSomethingWithCompletion:(void (^)())completion {
    void (^ExampleBlock)(NSString *) = [^{
        NSNotification *notification = [NSNotification notificationWithName:kExampleNotificationName object:nil userInfo:nil];
        [[NSNotificationCenter defaultCenter] postNotification:notification];

        if (completion) {
            completion();
        }
    } copy];

    // Async network call that calls ExampleBlock on either success or failure below...
}
0
répondu Nikolay Shubenkov 2017-04-11 12:58:45