2012-06-13 18 views
5

私はARCを使用していません。私はすべてのARCドキュメントを読みましたが、この質問に対する答えは見えませんでした。ARC、非ARC、および継承

-fobjc-arcでコンパイルされたモジュールに定義されているクラスがある場合、このクラスから新しいクラスを派生させることはできますか? ARC対応ではありませんか?

私の考えでは、派生クラスがルートクラスのivarsに触れない限り、正常に動作するはずです。 [super dealloc]を呼び出すdeallocメソッドを持っていても、派生クラスでは問題ありません。

そして、他の方法はどうですか?非ARCクラスからARC対応クラスを派生させることはできますか?うまくいくはずですね。

ボーナスポイント:私が気づかなければならないARCコードと非ARCコードを混在させると、何か問題がありますか?

答えて

6

私が知っている問題はありません。 ARCはソースコードのプリプロセッサのようなものであり、コンパイル時にメモリ管理の呼び出しを追加することに気づかなければなりません。リンクフェーズに到着すると、ARCコードを非ARCコードと実際に区別することはできません。派生クラスのメモリ管理が正しく、スーパークラスのメモリ管理が適切であれば、結果は正常に動作します(これはおそらく過度の単純化ですが、目的に応じて動作するはずです)。

私が考えることのできるのはweakのプロパティの扱いだけです。しかし、弱い性質を持つARCコードとMRCコードの組み合わせを使用してバグのあるコードに到達することが可能であるかどうかについては十分に分かりません。

+0

これはコンセンサスのようです。ご協力いただきありがとうございます。 – TomSwift

0

これはコメントですが、私はそれが何を言ったかを広げたいと考えていました。

通常のサブクラスからARCクラスを継承しようとしましたか?私の思考(どちらも試していない)は、これがうまくいかないことです。まず、ARCクラスにpublicプロパティまたはARCキーワードを使用するivarsがある場合は、weakのように、ヘッダファイルからのコンパイル時にエラーが発生すると思います。第二に、私はdeallocがどのように機能するのか分かりません。 [super dealloc]に電話する必要がありますか?知りません。

スーパークラスがARCの場合、どのサブクラスでもARCを使用しないのはなぜですか?それをすることに全く利点はありません。

非ARCクラスからARC対応クラスを派生させることはできますか?うまくいくはずですね。

私はどちらもうまくいかないと言っていましたが、私は間違っていました。事実上すべては手動参照であるNSObjectから継承しなければならない。

+0

たとえば、ARCを使用するライブラリにリンクしてメインプロジェクトを書き換えることができない場合など、MRCコードのARC対応クラスから派生することは理にかなっています。 (私はそれを行い、それは動作するように見えます)ARCの下で '[super dealloc]'を呼び出すことはできません、私はコンパイラがあなたのためにコールを挿入すると思います。 – zoul

+0

@zoul - はい、これも私たちが持っているシナリオです。サードパーティのモジュールにはARCが必要ですが、私たちは独自のモジュールにARCを使用する準備はできていません。 – TomSwift

+0

ARCクラスの非ARCサブクラスを持つことも意味があります。具体的には、サブクラスがCF APIを頻繁に使用する必要がある場合、ARCコードよりもMRRコードから行う方が簡単な場合があります。 – bbum

0

はい、ARC親クラスの非ARC祖先、および非ARC親クラスのARC祖先を実装できます。

実際には、ARCは構文砂糖です。つまり、コンパイルステップでソースコードを解析し、コードに適切な[リリース]と[保持]呼び出しを挿入するだけのプリプロセッサです。実行時レベルでは何も変更されません(weakプロパティを除く)。

0

ARCは、コンパイラはメモリ管理の面倒を意味し、非ARCは、あなたがそれの世話を意味するが、どちらの場合も、メモリ管理がまったく同じように動作します:オブジェクトは生き続ける必要がある場合は

  • を、そのカウンタを保持あなたがオブジェクトで行われている場合
  • (つまりreleaseが何をするかだ)のオブジェクトがこれ以上必要ない場合は、それへの参照が失われる前に、その保持カウンタが減少している(つまり、retainが何をするかだ)
  • 増加したが、さそれはまだ死んではいけません、例えばメソッドの結果として返す必要がある(そして死んだオブジェクトを返すことを望んでいない)ので、後で保持カウントを減らす自動解放プールに追加する必要があります(つまり、autoreleaseのようになります「将来、そのオブジェクトにreleaseを呼び出してください。」)
  • 新しく作成されたオブジェクトの保持カウントは、1です。
  • 保持カウントがゼロになると、オブジェクトは解放されます。

あなた自身で行っても、コンパイラがそれを行っても、それは何の役割も果たしません。コンパイル後、これらのメソッドはARCでも呼び出されますが、どのメソッドが呼び出されたときにコンパイラがARCを使用して決定しましたか。余計な魔法があります。 ARCは、メソッドの結果として返すときに自動解放プールにオブジェクトを追加する必要はありませんが、これはしばしば最適化されますが、呼び出し側と呼び出されたメソッドの両方が使用している場合にのみ適用されるアーク;そのうちの1つではない場合は、通常のautoreleaseが使用されます(ARCでは以前と同じように動作します)。

唯一注意しなければならないことは、サイクルを保持することです。 ARCを使用するかどうかに関係なく、参照カウントでは保持サイクルを処理できません。違いはありません。

落とし穴?フリーダイヤルで慎重にブリッジする。 A NSString *CFStringRefは実際には同じものですが、ARCはCF世界について知らないので、ARCは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オブジェクトが関数を返すまでオブジェクトを生かしておくために、ARCオブジェクトがCFオブジェクトを要求する関数にARCオブジェクトを渡すときに使用します。これは、常に安全である:

doSomethingWithString((__bridge CFStringRef)nsstr); 

ARCは、その行の下に何のコードが今までもうそれにアクセスしないようにいつでもnsstrを解放させた場合でも、この関数が戻ってきたし、関数の引数は、である前に、それは確かにそれを解放しません。関数が返るまで有効であることが保証されています(関数が文字列を生きたままにしたい場合、保持しなければならず、ARCは保持カウントがゼロにならないので解放しません)。

あなたは時々古いAPIとを持っているように、ほとんどの人はARCを渡しているに苦労するように見えるものは、void *コンテキストとしてオブジェクト、まだそれは、実際には死んでシンプルです。

- (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に(単なるポインタで、彼らは何もない、非ARCでIVARさんは、どちらもstrongweakあるとしてプロパティは、異なる

@interface SomeObject 
    @property (assign,nonatomic) id someIVAR; 
@end 

@implementation SomeObject 

- (void)someMethod { 
    id someValue = ...; 
    self.someIVAR = someValue; 
} 

注:非ARCのコードは、このいずれかのように動作します__unsafe_unretainedと呼ばれ、キーワードは不安全です)。

したがって、ivarsを直接使用し、setters/getterでプロパティにアクセスするコードを使用していない場合は、非ARCからARCに切り替えると、メモリ管理が正常に行われていたコードで保持サイクルが発生する可能性があります。一方、ARCから非ARCに移行すると、そのようなコードではポインタ(以前のオブジェクトへのポインタですが、オブジェクトはすでに死んでいるため、どこにも指し示さず、使用すると予測できない結果になります)以前は生き続けることができました。

関連する問題