J'ai un objet qui est conservé plus que nécessaire (probablement en raison d'une propriété qui est strong
au lieu de weak
). Grande base de code, il est donc difficile de trouver où.
Comment puis-je trouver toutes les lignes dans lesquelles cet objet est conservé lors de l'utilisation d'ARC?
Si je n'utilisais pas ARC, je suppose que je pourrais simplement remplacer retain
et vérifier d'où il s'appelle. Puis-je faire quelque chose de similaire avec ARC?
Pour suivre la croissance d'une application, Heapshot Analysis s'est révélé très efficace. Il capturera à la fois les vraies fuites et l'accumulation de mémoire là où les allocations ne sont pas prises en compte par les fuites.
Vous pouvez voir tous les événements de conservation/libération, et leur trace, à l'aide de l'instrument Allocations. Appuyez sur le petit bouton (i) de l'instrument Allocations et activez "Record reference count". L'activation de "Ne suivre que les allocations actives" réduit la quantité de données collectées par Instruments, ce qui la rend plus rapide (et les allocations mortes ne sont pas vraiment utiles dans ce contexte, mais peuvent l'être dans d'autres).
Avec cela, vous pouvez plonger dans n'importe quelle allocation (en cliquant sur la flèche droite dans le champ d'adresse), voir tous les événements de conservation/libération et voir exactement où ils se sont produits.
J'ai réussi à trouver le retain
incriminé en procédant comme suit:
-fno-objc-arc
à la classe d'objets Compiler Flags pour désactiver ARC pour cette classe.retain
(appelez simplement super
) et placez un point d'arrêt dessus.retain
est appelé.La semaine dernière, j'aidais des amis à déboguer des fuites dans leur projet ARC. Quelques conseils:
1/Construisez pour le profilage et démarrez des instruments avec détection de fuite. Explorez ensuite les objets actuellement alloués, trouvez l'objet que vous souhaitez (vous pouvez les trier par nom) et examinez son historique de conservation/libération. Notez que conserver le décompte n'est pas très utile avec ARC. Vous devez le vérifier manuellement étape par étape.
Essayez de commenter tout le code qui pourrait être la source de la fuite, puis décommentez-le pas à pas.
2/Mettez un NSLog
dans votre init
et dans votre dealloc
pour voir quand l'objet est créé et détruit.
3/Ne vous concentrez pas uniquement sur les définitions de propriétés, regardez si les paramétreurs de propriétés sont implémentés manuellement. J'ai trouvé un problème dans le projet de mes amis ressemblant à ceci:
@property (weak, nonatomic) id<...> delegate;
@interface ... {
id<...> _delegate;
}
@synthesize delegate = _delegate;
- (void)setDelegate(id<...>)delegate {
_delegate = delegate; //with ARC this retains the object!
}
Cette solution m'a été quelque peu utile. Il utilise essentiellement la méthode swizzling pour inciter le compilateur ARC à penser que vous ne remplacez pas la conservation et la libération.
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class cls = [self class];
// When swizzling a class method, use the following:
// Class class = object_getClass((id)self);
SEL originalSelector1 = NSSelectorFromString(@"retain");
SEL swizzledSelector1 = NSSelectorFromString(@"myretain");
SEL originalSelector2 = NSSelectorFromString(@"release");
SEL swizzledSelector2 = NSSelectorFromString(@"myrelease");
Method originalMethod1 = class_getInstanceMethod(cls, originalSelector1);
Method swizzledMethod1 = class_getInstanceMethod(cls, swizzledSelector1);
Method originalMethod2 = class_getInstanceMethod(cls, originalSelector2);
Method swizzledMethod2 = class_getInstanceMethod(cls, swizzledSelector2);
BOOL didAddMethod1 =
class_addMethod(cls,
originalSelector1,
method_getImplementation(swizzledMethod1),
method_getTypeEncoding(swizzledMethod1));
if (didAddMethod1) {
class_replaceMethod(cls,
swizzledSelector1,
method_getImplementation(originalMethod1),
method_getTypeEncoding(originalMethod1));
} else {
method_exchangeImplementations(originalMethod1, swizzledMethod1);
}
BOOL didAddMethod2 =
class_addMethod(cls,
originalSelector2,
method_getImplementation(swizzledMethod2),
method_getTypeEncoding(swizzledMethod2));
if (didAddMethod2) {
class_replaceMethod(cls,
swizzledSelector2,
method_getImplementation(originalMethod2),
method_getTypeEncoding(originalMethod2));
} else {
method_exchangeImplementations(originalMethod2, swizzledMethod2);
}
});
}
-(id)myretain {
#pragma clang diagnostic Push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
NSLog(@"tracking retain now %d",(int)[self performSelector:NSSelectorFromString(@"retainCount")]);
SEL selector = NSSelectorFromString(@"myretain");
return [self performSelector:selector withObject:nil];
#pragma clang diagnostic pop
}
-(id)myrelease {
#pragma clang diagnostic Push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
NSLog(@"tracking release now %d",(int)[self performSelector:NSSelectorFromString(@"retainCount")]);
SEL selector = NSSelectorFromString(@"myrelease");
return [self performSelector:selector withObject:nil];
#pragma clang diagnostic pop
}