ARCの使用およびメモリ管理

7320 ワード

ARCの使用やメモリ管理については、公式ドキュメントを参照することを強くお勧めします.メモリ管理の原理について詳しく紹介されています.MRCを使ったことがある人は必ずこれを見たことがあると思います.
簡単で実用的なARC使用チュートリアルもあります:ARC Best Practices
 2011  WWDC ,    90% crash          ,ARC(Automatic Reference Counting)           。  ARC ,            ,           (  ARC      ,   iOS     ,              )。
    ,         ,              ,        MRC       ,        ,           。

ARCはほとんどのメモリ漏洩の問題を解決できますが、注意しなければならない点があります.
循環参照
                      , retain   ,                  。      delegate    weak    retain strong,         ,          。

この簡単なループ参照はcodingの過程で注意すれば,一般的に発見できる.
解決策も簡単で、一般的にはループチェーンの強い参照を弱い参照に変更すれば解決できます.
もう1つのblockによる循環参照問題は、通常、blockの原理にあまり詳しくない開発者が発見しにくい問題である.
blockによるループ参照
まず、block呼び出しに関する公式ドキュメントの説明を見てみましょう.Object and Block Variables
When a block is copied, it creates strong references to object variables used within the block. If you use a block within the implementation of a method:

    If you access an instance variable by reference, a strong reference is made to self;
    If you access an instance variable by value, a strong reference is made to the variable.

主に2つのルールがあります.
最初のルールは、blockでプロパティにアクセスすると、blockがselfにretainします.
第2のルールでは、blockでローカル変数にアクセスすると、blockはその変数に強い参照、すなわちretainローカル変数を持つ.
この2つのルールに基づいて、ループ参照が発生した場合を知ることができます.
//  1
self.myblock = ^{
    [self doSomething];           //       
    NSLog(@"%@", weakSelf.str);   //     
};

//  2
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setCompletionBlock:^{
  NSString* string = [request responseString];
}];

オブジェクトはblockに対して強い参照を持ち、block内部は外部オブジェクトに対して強い参照を持ち、閉ループを形成し、メモリ漏洩が発生する.
このメモリの漏洩をどのように解決しますか?
block変数で解決できるのは、まず公式ドキュメントを見てみましょう.
Use Lifetime Qualifiers to Avoid Strong Reference Cycles: https://developer.apple.com/library/ios/releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html
In manual reference counting mode, __block id x; has the effect of not retaining x. In ARC mode, __block id x; defaults to retaining x (just like all other values). To get the manual reference counting mode behavior under ARC, you could use __unsafe_unretained __block id x;. As the name __unsafe_unretained implies, however, having a non-retained variable is dangerous (because it can dangle) and is therefore discouraged. Two better options are to either use __weak (if you don’t need to support iOS 4 or OS X v10.6), or set the __block value to nil to break the retain cycle.

公式サイトはいくつかの方案を提供して、私達は第1種を見て、__を使いますblock変数:
MRCではblock id xはretainがxに住まない.しかしARCではデフォルトではretainがxに住んでいます.unsafe_を使用する必要があります.unretained __block id xは弱い参照の効果を達成します.
ソリューションは次のようになります.
__block id weakSelf = self;  //MRC
//__unsafe_unretained __block id weakSelf = self;   ARC     
self.myblock = ^{
    [weakSelf doSomething];  
    NSLog(@"%@", weakSelf.str);  
};

performSelectorの問題[self performSelector:@selector(foo:)withObject:self.property afterDelay:3];performSelectorの遅延呼び出しの原理は、上記の関数を実行すると自動的にselfが表示される.propertyのretainCountに1を加えると、selectorが実行されるまでselfが使用されない.propertyのretainCountは1を減らします.このようにselectorが実行されていない場合、selfは解放されず、メモリ漏洩として撮影される可能性があります.より良い解決策は、実行されていないperformをキャンセルすることです:[NSObject cancelPreviousPerformRequestsWithTarget:self];このような原因で発生した漏洩は、いかなる規則にも違反していないため、Intructionでは発見できない.
NSTimerの問題
timerが将来のある時点で1回または複数回私たちが指定した方法を実行するために使用されることを知っています.問題が発生しました(もちろん掘削機ではありません).いったいシステムはtimerがactionをトリガするとき、私たちが指定した方法が有効であることをどのように保証しているのでしょうか.もしreceiverが無効になったら?
     ,     retain     ,            。

公式のドキュメントを見てみましょう.自分でdemoテストを書くことをお勧めします.
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)seconds target:(id)target selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)repeats

target  
The object to which to send the message specified by aSelector when the timer fires. The timer maintains a strong reference to target until it (the timer) is invalidated. (            timer  invalidated)

userInfo  
The user info for the timer. The timer maintains a strong reference to this object until it (the timer) is invalidated. This parameter may be nil.

repeats 
If YES, the timer will repeatedly reschedule itself until invalidated. If NO, the timer will be invalidated after it fires.

repeatsパラメータは、使い捨て(repeatsがNO)のtimerが再トリガーされた後にinvalidatedが自動的に呼び出され、重複するtimerは呼び出されないことに注意してください.また問題が発生しました.次のコードを見てください.
- (void)dealloc
{
    [timer invalidate];
    [super dealloc];
}

これは犯しやすいエラーです.このtimerが重複するtimerであれば、selfオブジェクトはtimerretainに住まれ、このときinvalidateを呼び出さないと、selfオブジェクトの参照カウントは1より大きくなり、deallocは永遠に呼び出されず、メモリ漏洩が発生します.
timer     target  retain,          target       ,       timer,       dealloc    invalidate。

timerについては、runloopで有効にしなければならないなど、研究できることがたくさんあります.例えば、時間は正確ですか.これらは本章のテーマとは関係ないので,しばらくは言わない.
performSelector:afterDelayについて
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay

私たちはやはり公式文書を見て、同じようにdemo検証を書いてほしいと思っています.
This method sets up a timer to perform the aSelector message on the current thread’s run loop. The timer is configured to run in the default mode (NSDefaultRunLoopMode). When the timer fires, the thread attempts to dequeue the message from the run loop and perform the selector. It succeeds if the run loop is running and in the default mode; otherwise, the timer waits until the run loop is in the default mode.

システムはtimerに依存して遅延トリガを保証しますが、runloopがdefault modeにある場合にのみ実行に成功します.そうしないと、selectorはrun loopがdefault modeに切り替わるのを待っています.私たちが以前timerについて話していたように、ここでperformSelector:afterDelay:を呼び出すと、targetへのシステムの強い参照、つまりretainが住むことになります.このように、selectorがずっと実行できない場合(例えばrunloopはdefault modelの下で実行されていない)、targetが解放されず、メモリ漏洩が発生します.
どうやってこの問題を解決しますか?
実は簡単です.適切な時に呼び出しをキャンセルすればいいです.システムはインタフェースを提供しています.
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget

この関数はdeallocで呼び出すことができますか?自分で考えてもいいですか?
NSNotificationのaddObserverとremoveObserverの問題について
私たちはよくdeallocでremoveObserverを呼び出すことに気づくはずですが、上記の問題はありませんか?
答えは否定的です.これはaddObserverが受信者に弱い参照を確立するだけなので、メモリ漏洩の問題は発生しません.しかし、deallocでremoveObserverを呼び出す必要があります.通知を避けると、オブジェクトが破棄され、crashが発生します.
C言語のインタフェース
C言語はOCのretainとreleaseを呼び出すことができず、一般的なC言語インタフェースはrelease関数(例えばCGcontextRelease(context c))を提供してメモリを管理する.ARCはこれらのCインタフェースの関数を自動的に呼び出すので、これはやはり私たち自身が管理する必要がある.
次に、releaseインタフェースを自分で呼び出す必要がある一般的なペイントコードを示します.
CGContextRef context = CGBitmapContextCreate(NULL, target_w, target_h, 8, 0, rgb, bmi);

    CGColorSpaceRelease(rgb);

    UIImage *pdfImage = nil;
    if (context != NULL) {
        CGContextDrawPDFPage(context, page);

        CGImageRef imageRef = CGBitmapContextCreateImage(context);
        CGContextRelease(context);

        pdfImage = [UIImage imageWithCGImage:imageRef scale:screenScale orientation:UIImageOrientationUp];
        CGImageRelease(imageRef);
    } else {
       CGContextRelease(context);
    }

総じて言えば、ARCは使いやすく、メモリの漏洩の問題の大部分を解決するのに役立ちます.ですから、ARCをそのまま使うことをお勧めします.できるだけmrcを使わないでください.