オブジェクトがARCで保持されている場所を見つける
必要以上に保持されているオブジェクトがあります(strong
の代わりにweak
である可能性が高いため)。大きなコードベースなので、どこを見つけるのは難しいです。
ARCを使用しているときに、このオブジェクトが保持されているすべての行を見つけるにはどうすればよいですか?
ARCを使用していなかった場合は、retain
をオーバーライドして、それが呼び出された場所から確認できると思います。 ARCで同様のことができますか?
アプリケーションの成長を追跡するには、 Heapshot Analysis が非常に効果的であることが証明されています。割り当てがリークによって考慮されていない場合、真のリークとメモリの付加の両方をキャプチャします。
Allocations計測器を使用して、すべての保持/解放イベントとそれらのバックトレースを確認できます。 Allocationsインストゥルメントの小さな(i)ボタンを押して、「Record reference counts」をオンにします。 「アクティブな割り当てのみを追跡する」をオンにすると、Instrumentsによって収集されるデータ量が減り、より迅速になります(デッド割り当ては、このコンテキストではあまり役に立ちませんが、他の場合でも有効です)。
これにより、(アドレスフィールドの右矢印をクリックして)任意の割り当てに飛び込み、すべての保持/解放イベントを確認し、それらが発生した場所を正確に確認できます。
次の操作を行うことで、問題のあるretain
を見つけることができました。
- 一時的に
-fno-objc-arc
をオブジェクトクラスのコンパイラフラグに追加して、そのクラスのARCを無効にします。 - 一時的に
retain
をオーバーライドし(super
を呼び出す)、それにブレークポイントを設定します。 retain
が呼び出されるたびに、コールスタックをデバッグおよびチェックします。
先週、私は何人かの友人が彼らのARCプロジェクトのリークをデバッグするのを手伝っていました。いくつかのヒント:
1 /プロファイリング用にビルドし、リーク検出を使用して機器を起動します。次に、現在割り当てられているオブジェクトを調べて、目的のオブジェクトを見つけ(名前で並べ替えることができます)、その保持/リリース履歴を調べます。 ARCでは、保持カウントはあまり役に立たないことに注意してください。ステップごとに手動で確認する必要があります。
リークの原因となる可能性のあるすべてのコードをコメント化してから、ステップごとにコメントを外してください。
2/NSLog
をinit
とdealloc
に入れて、オブジェクトが作成および破棄されるタイミングを監視します。
3 /プロパティの定義だけを見るのではなく、プロパティセッターが手動で実装されているかどうかを確認します。友達のプロジェクトで次のような問題を見つけました。
@property (weak, nonatomic) id<...> delegate;
@interface ... {
id<...> _delegate;
}
@synthesize delegate = _delegate;
- (void)setDelegate(id<...>)delegate {
_delegate = delegate; //with ARC this retains the object!
}
この解決策は私にとっていくらか役立ちました。基本的に、スウィズリングメソッドを使用して、ARCコンパイラーを保持およびリリースをオーバーライドしていないと考えさせます。
+ (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
}