2015-10-21 2 views
3

Pourquoi l'analyseur statique ne détecte-t-il pas les références circulaires avec des blocs? Je me souviens que cela avait l'habitude de le faire quand j'avais retenu mes délégués au lieu de les assigner, avant l'introduction des blocs. Je me souviens des jolies petites lignes qu'il dessinait sur mon code (je pense ...)Références circulaires en Objective-C et clang

Si je fais cela, sans utiliser weakSelf, je sais que je vais obtenir une référence circulaire.

// Note 1: myObject is 'retained' by self. 
// Note 2: myObject retains the block for the future 

[self.myObject registerBlockOfCodeForFutureExectution:^{ 
    [self doSomething]; 
}]; 

Sample Project Exploiting Issue

Maintenant, si je sais, et je suis un homme stupide, alors pourquoi ne sait pas mon ordinateur intelligente ce qui est mauvais et me prévenir que je suis stupide?

Il doit y avoir une raison logique pour laquelle il ne peut pas le détecter, et je veux savoir quelle est cette raison.

Cette question concerne l'analyse statique et statique, ne suggérez pas comment je répare les références circulaires - je sais comment faire.

+1

Il se peut qu'il ne crée pas de cycle de rétention, le bloc conserve-t-il automatiquement le bloc? Ça ne ressemble pas à ça. – zaph

+1

* "Je sais que je vais obtenir une référence circulaire" * - mauvaise hypothèse. – rmaddy

+1

Dans ce cas le bloc est en ligne, seul le scope tient le bloc, il n'y a pas de cycle de rétention. Un test simple consiste à placer un NSLog() dans une méthode dealloc dans la classe et à voir qu'il est appelé lorsque l'instance de la classe perd sa valeur. – zaph

Répondre

2

Si vous utilisez self à l'intérieur du bloc, cela ne signifie pas automatiquement que vous obtenez un cycle de rétention. Vous obtenez le cycle de conservation seulement si la durée de vie du bloc dépend de la durée de vie de l'objet self. Cela peut être le cas si self a une forte référence à myObject ou des dépendances plus complexes sont également possibles (je suppose qu'il «sauve» en effet le bloc passé à une méthode, donc vous y avez déjà une forte référence). Donc, pour avoir un cycle de conservation dans votre exemple, vous devez avoir deux conditions suivantes rencontrées (aucune d'elles ne vient du code que vous avez publié), et le compilateur doit en être conscient: 1. La durée de vie de myObject est lié à l'auto - laisse supposer que l'auto a une forte référence à elle 2. saveThisBlockInMyObject: conserve le bloc qui lui sont transmises

J'ai fait un petit échantillon qui donne compilateur avertissement sur la capture auto - pour aborder le 1er point, je déclare myObject comme une propriété forte d'une classe:

@property (strong) MyTestClass* myObj; 
... 
self.myObj = [MyTestClass new]; 

Pour le second point, je n'ai pas trouvé de moyen de spécifier que la méthode conserve son argument (il y a source annotations pour les valeurs retournées, mais il n'y a pas d'annotations pertinentes pour les paramètres de la méthode). Mais vous déclarez bloc comme une propriété forte de votre classe de test, puis compilateur est heureux de vous mettre en garde le cycle possible de retenir:

typedef void (^MyVoidBlock)(); 
// MyTestClass 
@property (nonatomic, copy) MyVoidBlock voidBlock; 

self.voidBlock = ^{ 
    [self doSomething]; // Warning! 
}; 

espoir qui fait sens :)

+0

Mais dans le cas de l'OP, le bloc n'est pas une propriété, il est en ligne, seule la portée tient le bloc. Un test simple consiste à placer un 'NSLog()' dans une méthode dealloc dans la classe et à voir qu'il est appelé quand l'instance de la classe perd sa valeur. – zaph

+0

c'est ce dont je parlais - nous devons dire au compilateur que le bloc est retenu dans la méthode, mais la seule façon que je pourrais trouver pour spécifier est pour les propriétés (voir le lien des annotations source) – Vladimir

+0

Oui, et expliquent beaucoup, ajouter un cas avec un bloc conservé par une propriété, ce qui n'est pas le cas dans la question. Mon explication serait qu'il ne peut pas y avoir de pointeur sur le bloc s'il est en ligne et ainsi l'utilisation de self dans un bloc en ligne ne provoquera pas de cycle de retenue. – zaph

1

Le code affiché à github ne provoque une conserver le cycle.

actuel Code github:

@interface MyObject() 
@property (nonatomic, copy) dispatch_block_t codeToRunInFuture; 
@end 

@implementation MyObject 

- (void) registerBlockForFuture:(dispatch_block_t)block { 
    self.codeToRunInFuture = block; 
} 

// Call in ViewController 
self.myObject = [MyObject.alloc init]; 
[self.myObject registerBlockForFuture:^{ 
    [self runThisInFuture]; 
}]; 

je peux voir où cela serait difficile à attraper car l'analyseur ne peut pas savoir ce que block peut être et ne peut donc pas dire s'il y a une référence auto forte ou faible . Il devrait examiner tous les cas où registerBlockForFuture: est appelé et le block dans chaque cas.

La réponse pourrait être de soumettre un bugreport à Apple.