ARC はコンパイラがメモリ管理を処理することを意味し、非 ARC はユーザーが処理することを意味しますが、どちらの場合もメモリ管理はまったく同じように機能します。
- オブジェクトが生き続ける必要がある場合、その保持カウンターが増加します (それが
retain
機能します) 。
- オブジェクトが不要になった場合、オブジェクトへの参照が失われる前に保持カウンターが減少します (これが原因です
release
) 。
- オブジェクトを使い終わったが、まだ死んではいけない場合、たとえば、メソッドの結果として返す必要がある (そして死んだオブジェクトを返したくない) 場合は、減少する自動解放プールに追加する必要があります。後でその保持カウントを保持します (それは、「将来のある時点でそのオブジェクトを
autorelease
呼び出す」と言っているようなものです)。release
- 新しく作成されたオブジェクトの保持カウントは
1
です。
- 保持カウントがゼロになると、オブジェクトは解放されます。
すべてを自分で行う場合でも、コンパイラが行う場合でも、それは何の役割も果たしません。コンパイル後、これらのメソッドは ARC でも呼び出されますが、ARC ではコンパイラがいつどのメソッドが呼び出されるかを決定します。追加の魔法があります。たとえば、ARC はオブジェクトをメソッドの結果として返すときに常に自動解放プールにオブジェクトを追加する必要はありません。これは多くの場合最適化して取り除くことができますが、この魔法は呼び出し元が呼び出されたメソッドは両方とも ARC を使用しています。それらのいずれかがそうでない場合は、法線autorelease
が使用されます (ARC でも以前とまったく同じように機能します)。
注意しなければならない唯一のことは、サイクルを保持することです。ARC を使用するかどうかに関係なく、参照カウントは保持サイクルを処理できません。ここに違いはありません。
落とし穴?フリーダイヤルブリッジングには注意してください。ANSString *
と aCFStringRef
は実際には同じものですが、ARC は CF ワールドについて認識していませNSString
んCFString
。ARC を使用する場合は、ブリッジする方法を ARC に伝える必要があります。
CFStringRef cfstr = ...;
NSString * nsstr = (__bridge_transfer NSString *)cfstr;
// NSString * nsstr = [(NSString *)cfstr autorelease];
上記のコードは、「ARC、そのCFString
オブジェクトの所有権を取得し、使い終わったらすぐに解放してください」という意味です。このコードは、以下のコメントに示されているコードのように動作します。非常に注意しcfstr
て、保持カウントを少なくとも 1 つ持つ必要があり、ARC はそれを少なくとも 1 回は解放しますが、まだです。逆に:
NSString * nsstr = ...;
CFStringRef cfstr = (__bridge_retained CFStringRef)cftr;
// CFStringRef cfstr = (CFStringRef)[nsstr retain];
上記のコードは、「ARC、その所有権を私に与えてくださいNSString
。完了したらリリースします」という意味です。もちろん、その約束は守らなければなりません!ある時点で呼び出す必要がありCFRelease(cfstr)
ます。そうしないと、メモリリークが発生します。
最後に(__bridge ...)
、これは単なる型キャストであり、所有権は譲渡されません。この種のキャストは、キャスト結果を保持しようとするとダングリング ポインターが作成される可能性があるため危険です。通常、ARCオブジェクトをCFオブジェクトを期待する関数にフィードするときに使用します。ARCは、関数が戻るまでオブジェクトを確実に存続させるためです。たとえば、これは常に安全です。
doSomethingWithString((__bridge CFStringRef)nsstr);
その行より下のコードがもうアクセスしないため、ARC がnsstr
いつでも解放されたとしても、この関数が戻る前に解放されることはなく、関数の引数は定義上、関数が戻るまで存続することが保証されているだけです (関数は文字列を存続させたいので、それを保持する必要があり、保持カウントがゼロにならないため、解放後に ARC は割り当てを解除しません)。
ほとんどの人が苦労しているように見えるのは、ARC オブジェクトをvoid *
コンテキストとして渡すことです。これは、古い API で必要になることがありますが、実際には非常に単純です。
- (void)doIt {
NSDictionary myCallbackContext = ...;
[obj doSomethingWithCallbackSelector:@selector(iAmDone:)
context:(__bridge_retained void *)myCallbackContext
];
// Bridge cast above makes sure that ARC won't kill
// myCallbackContext prior to returning from this method.
// Think of:
// [obj doSomethingWithCallbackSelector:@selector(iAmDone:)
// context:(void *)[myCallbackContext retain]
// ];
}
// ...
- (void)iAmDone:(void *)context {
NSDictionary * contextDict = (__bridge_transfer NSDictionary *)context;
// Use contextDict as you you like, ARC will release it
// prior to returning from this method. Think of:
// NSDictionary * contextDict = [(NSDictionary *)context autorelease];
}
そして、私はあなたのために、一見するとそれほど明白ではない大きな問題を抱えている必要があります。このコードを検討してください:
@implementation SomeObject {
id _someIVAR;
}
- (void)someMethod {
id someValue = ...;
_someIVAR = someValue;
}
このコードは、ARC と非 ARC で同じではありません。ARC では、すべての変数がデフォルトで強力であるため、ARC では、このコードは次のコードと同じように動作します。
@interface SomeObject
@property (retain,nonatomic) id someIVAR;
@end
@implementation SomeObject
- (void)someMethod {
id someValue = ...;
self.someIVAR = someValue;
}
割り当てsomeValue
はそれを保持し、オブジェクトは生き続けます! 非 ARC では、コードは次のように動作します。
@interface SomeObject
@property (assign,nonatomic) id someIVAR;
@end
@implementation SomeObject
- (void)someMethod {
id someValue = ...;
self.someIVAR = someValue;
}
非 ARC の ivar はどちらstrong
でもないため、プロパティが異なることに注意してくださいweak
。それらは何もなく、単なるポインターです (ARC では呼び出され__unsafe_unretained
、ここのキーワードはunsafeです)。
そのため、ivar を直接使用し、setter/getter でプロパティを使用せずにそれらにアクセスするコードがある場合、非 ARC から ARC に切り替えると、以前は適切なメモリ管理を行っていたコードでリテイン サイクルが発生する可能性があります。一方、ARC から非 ARC に移行すると、そのようなコードはダングリング ポインター (以前のオブジェクトへのポインターですが、オブジェクトは既に終了しているため、これらはどこにもポイントせず、それらを使用すると予測できない結果になります) を引き起こす可能性があります。予期せず死ぬかもしれません。