継続物品本明細書中で、この主通知にと一緒にメモリリークを使用して、KVOオブザーバ、ブロック循環参照を削除しないでNSThreadと実行ループを引き起こしました。
図1に示すように、メモリリークによる通知
1.1、ios9後、一般的な通知は、もはや手動でオブザーバを削除する必要がない、システムが自動的に[:自己[NSNotificationCenter defaultCenter] removeObserver]の時のdeallocを呼び出します。必要な前に手動で削除するIos9。
その理由は次のとおりです。ios9は以前にオブザーバを登録したときに、視聴者がリサイクルされたときに手動で削除知らせていない場合、観察者は、その後にポインタを操作を保持しないオブジェクトの中心に気づくが、unsafe_unretained参照して、そうしますフィールドガイド、あなたは通知を送信し、次の時間になります回復したメモリ領域が、それはプログラムがクラッシュします。
ios9中心も通知時間なしで手動で観察ブランキング後に自動的にポインタから取り出し、観察者の通知弱い弱参照を開始する回収され、その後、再送通知メッセージはヌルポインタに送信されません問題が発生します。
このAPIの使用は保持システムにより観察者につながるので1.2は、ブロックモード通知リスニングを使用して、まだ、処理する必要があります。
次のコードを考えてみます。
[NSNotificationCenter defaultCenter] addObserverForName:@ "notiMemoryLeak"オブジェクト:ゼロ・キュー:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull注){ のNSLog( "11111" @)。 }]。 //发个通知 [[NSNotificationCenter defaultCenter] postNotificationName: "notiMemoryLeak"オブジェクト@:nilを];
最初の印刷は、第二、二回のプリントで3回を印刷する第三の時間が来た、最初に入っています。あなたはデモアドレスは記事の下部を参照して、デモを試すことができます。
解決策は、受信者の通知を記録することで、受信者が同じようにdealloc内で削除されます。
@property(非アトミック、強い)IDオブザーバ。
self.observer = [NSNotificationCenter defaultCenter] addObserverForName:@ "notiMemoryLeak"オブジェクト:ゼロ・キュー:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull注){ のNSLog( "11111" @)。 }]。 //发个通知 [[NSNotificationCenter defaultCenter] postNotificationName: "notiMemoryLeak"オブジェクト@:nilを];
- (ボイド)のdealloc { [NSNotificationCenter defaultCenter] removeObserver:self.observer名:@ "notiMemoryLeak"オブジェクト:なし]。 NSLog( "やあ、我にdealloc了啊" @)。 }
図2は、KVOは、メモリリークに起因します
2.1、現在一般的KVOを使用され、観測者が削除されていない場合でも、問題はそこではありません
、次のコードを考えてみましょう:
- (無効){kvoMemoryLeak MFMemoryLeakView見る* = [[MFMemoryLeakViewのalloc] initWithFrame:self.view.bounds]; [self.view addSubview:ビュー]; [addObserver見る:自己forKeyPath:NSKeyValueObservingOptionNewコンテキスト:nilを"フレーム"のオプション@を]; //これらの2つの原則を呼び出すためのイニシアチブをとるが、後半KVOで特定のKVOにインスピレーションを与えてきた詳細説明 [表示willChangeValueForKey: "フレーム" @]; [ビューdidChangeValueForKey: "フレーム" @]; } - (無効)observeValueForKeyPath :( NSStringの*)キーパスofObject:(ID)オブジェクト変更:(NSDictionaryの<NSKeyValueChangeKey、ID> *)の変更コンテキスト:(void *型)コンテキスト{ ([isEqualToStringとしてキーパス: "フレーム" @] IF){ のNSLog(ビュー」@ =% @」、オブジェクト); }
}
この状況は問題、私は推測がされません削除されていないビューは、コントローラも破壊されたときに破壊され、その枠のビューは何の問題もないだろうオブザーバを削除しない、変更されませんので、私がしたので、容疑者、観測されたオブジェクトは何が起こるかを破壊しないものである場合には?オブザーバーが破壊されている場合は、オブジェクトも観察変更され、問題があるでしょうか?
2.2オブジェクトが破壊されることはありません観察、観測者を削除しない、不確実な崩壊が発生します。
次に上記の憶測、最初のシングルトンオブジェクトMFMemoryLeakObjectを作成し、プロパティのタイトルがあります:
@interface MFMemoryLeakObject:NSObjectのの *タイトルをNSStringの@property(アトミック、コピー); +(MFMemoryLeakObject *)sharedInstance。 @end の#import "MFMemoryLeakObject.h" @implementation MFMemoryLeakObject +(MFMemoryLeakObject *)sharedInstance { 静的MFMemoryLeakObject * sharedInstance =ゼロ、 静的dispatch_once_t onceToken。 dispatch_once(&onceToken、^ { sharedInstance = [自己ALLOC] INIT]; sharedInstance.title = @ "1"; }); sharedInstanceを返します。 } @end
そして、MFMemoryLeakViewでのtitle属性MFMemoryLeakObjectのために聞きます:
#importを"MFMemoryLeakView.h" の#import "MFMemoryLeakObject.h" @implementation MFMemoryLeakView - (instancetype)initWithFrame:(CGRect)フレーム{ ([スーパーinitWithFrame:フレーム] =自己)であれば{ self.backgroundColor = [UIColor whiteColor]。 【自己viewKvoMemoryLeak]。 } 自己を返します。 } の#pragmaマーク- 6.KVO造成的内存泄漏 - (ボイド)viewKvoMemoryLeak { [MFMemoryLeakObject sharedInstance] addObserver:自己forKeyPath:@ "タイトル"オプション:NSKeyValueObservingOptionNewコンテキスト:なし]。 } - (ボイド)observeValueForKeyPath:(NSStringの*)キーパスofObject:(ID)オブジェクト変更:(NSDictionaryの<NSKeyValueChangeKey、ID> *)変更コンテキスト(ボイド*)コンテキスト{ ([キーパスisEqualToString:@ "タイトル"])であれば{ のNSLog(@ "[MFMemoryLeakObject sharedInstance] .TITLE =%@"、[MFMemoryLeakObject sharedInstance] .TITLE)。
コントローラ内のタイトルの値における最後の変更は、正面図で変更を破壊した後、一度破壊を変更されました:
//6.1、在MFMemoryLeakView监听一个单例对象 MFMemoryLeakView *ビュー= [[MFMemoryLeakView ALLOC] initWithFrame:self.view.bounds]。 [self.viewのaddSubview:ビュー]。 【MFMemoryLeakObject sharedInstance] .TITLE = "2" @。 dispatch_after(dispatch_time(DISPATCH_TIME_NOW、(int64_tの)(1.0 * NSEC_PER_SEC))、dispatch_get_main_queue()、^ { [図removeFromSuperview]; [MFMemoryLeakObject sharedInstance] .TITLE = @ "3"; });
試みの後、初めて問題はないため、崩壊上の第二が発生し、エラーフィールドガイド特定の我々は、デモとテストを行うことができ、デモ・アドレスは下を参照してください。
解決策は次のように観察者の視野のdeallocメソッドを削除し、簡単です:
- (ボイド)のdealloc { [MFMemoryLeakObject sharedInstance] removeObserver:自己forKeyPath:@ "タイトル"]。 NSLog(@ "やあ、我MFMemoryLeakViewにdealloc了啊"); }
一般的には、そこに観察しているコードや仕様を書くには、そうでない場合は、プロジェクトがバグYuxianyusiのさまざまなを生成する可能性が高いでした、削除する必要があります。KVOクラッシュを引き起こす削除重複もありますが、この記事を参照してください。https://www.cnblogs.com/wengzilin/p/4346775.htmlを。
図3に示すように、メモリリークに起因するブロック
のブロックは、メモリリークは、一般的範囲内で所有者をブロック循環参照され、ブロックを引用し、このようにして所有者がメモリを解放することはないブロックにつながる引き起こさ。
シーンブロックメモリリーク分析と解決策を説明します。この記事では、他のブロックの原理は、ブロックの後に単一の章で説明します。
循環参照を引き起こす自己またはメンバー変数内部と呼ばれる属性として3.1、ブロック、。
ブロックのプロパティを定義する、次のコードを検討してください。
typedefは、ボイド(^ BlockType)(無効)。 @interface MFMemoryLeakViewController() @property(非アトミックコピー)BlockTypeブロック。 @property(非アトミック、割り当てる)NSIntegerタイマーカウント。 @終わり
次に呼び出します。
#pragmaマーク- 7.blockメモリリークが生じ - (ボイド){blockMemoryLeak // 7.1通常円形の参照ブロック self.block = ^(){ のNSLog(@ "%@ = MFMemoryLeakViewController"、自己); のNSLog(MFMemoryLeakViewController」@ %ZD =」_タイマーカウント); }; self.block(); }
これは、サイクルブロックとコントローラの参照を作成するソリューションが簡単で、MRCで__blockを使用し、使用__weakは、ARCでメンバ変数を閉鎖 - >アクセスを解消することができます。
放出された場合、オブジェクトはそのような挿入の配列や辞書などのいくつかの問題を引き起こす可能性がブロックの実行中にゼロとなり、唯一の変更されたオブジェクトが__weakことに留意すべきです。
ブロックで仕上げながら処理ブロックの実行は、オブジェクトは、ゼロに設定されていないように、このターゲットはなりARC、再びブロック内の強い参照を修飾標的を__weakすることが提案されています自動リリースは、循環参照が発生することはありません。
typeof演算__weak(セルフ)weakSelf =自己; = ^()self.blockは{ //強い参照について追加することを提案しているが、weakSelfが解放されないよう 、__strong typeof演算(weakSelf)strongSelf = weakSelf のNSLog(@ "MFMemoryLeakViewController =%@"、strongSelfを); のNSLog( "%MFMemoryLeakViewController = ZD" @、strongSelf - > _タイマーカウント); }; self.block()。
3.2は、NSTimerは、時間のブロックを使用して作成した循環参照に注意を払います
このコードを見てください:
[NSTimer scheduledTimerWithTimeInterval:1.0リピート:YESブロック:^(NSTimer * _Nonnullタイマー){ のNSLog(@ "MFMemoryLeakViewController =%@"、自己)。 }]。
ビューのブロックの視点からは、何の循環参照は、実際には、このクラスのメソッド内で、セルフタイマーへの強い参照が存在しない、と彼らはほかに、閉ループ__weakカットを使用する必要がありますので、タイマーは、このようにして作成された繰り返しありYESは、プロセスを無効にする必要がある、またはタイマーやストップしたとき。
@property(アトミック、強い)NSTimer *タイマー。
__weak typeof演算(自己)weakSelf =自己。 _timer = [NSTimer scheduledTimerWithTimeInterval:1.0リピート:YESブロック:^(NSTimer * _Nonnullタイマ){ のNSLog(@ "MFMemoryLeakViewController =%@"、weakSelf)。 }]。
- (ボイド)のdealloc { [無効_timer]; のNSLog(@ "HI、I MFMemoryLeakViewControllerのdealloc、ああ"); }
図4は、NSThreadは、メモリリークに起因します
NSThreadと実行ループは、循環参照の問題に注意を払う、組み合わせて使用し、以下のコードを考えてみます。
- (ボイド)threadMemoryLeak { NSThread *スレッド= [[NSThread ALLOC] initWithTarget:自己セレクタ:@selector(threadRun)目的:なし]。 [スレッド開始]。 } - (ボイド)threadRun { [NSRunLoop currentRunLoop]のaddport:[NSPort ALLOC] INIT] forMode:NSDefaultRunLoopMode]。 [[NSRunLoop currentRunLoop]実行]; }
このコード行を、問題の原因は、「[[NSRunLoop currentRunLoop]実行]」ということです。その理由は、実行方法NSRunLoopが止められていることであり、破壊されたことがないスレッドを開くように設計されているだけでなく、電流制御(自己)の電流に作成されたスレッドは、そう循環参照を引き起こし、強い参照した場合。
ソリューションは、様々な方法を作成するときにブロックを使用して作成されます。
- (ボイド)threadMemoryLeak { NSThread *スレッド= [[NSThread ALLOC] initWithBlock:^ { [[NSRunLoop currentRunLoop]のaddport:[NSPort ALLOC] INIT] forMode:NSDefaultRunLoopMode]。 [[NSRunLoop currentRunLoop]実行]; }]。 [スレッド開始]。 }
コントローラは、解放することができ、実際には、このスレッドは破壊されない、コール「CFRunLoopStop(CFRunLoopGetCurrentは());」場合でもなるように、これはこのときだけ実行ループを停止することができますので、このスレッドを停止することはできません、次のサイクルがまだ継続することができます続行。具体的な解決策は、私は、単一の章実行ループの中で説明します。
メモリリークの分析は、私は限られたレベルですので、多くの場所はまだ正しいとあなたを歓迎するために十分な深さを話すことができなかった、あなたへの書き込み。
demo地址: https://github.com/zmfflying/ZMFBlogProject.git