6

ARCでは、outパラメータは次の形式を取ります(デフォルトでは、これはと同等ですNSError **)。

- (BOOL)tryWithError:(NSError *__autoreleasing *)err;

ARCリリースノートへの移行から、ローカル変数のアドレスを渡す__strongと、コンパイラは一時変数を作成し、次のコードを生成します。

NSError *error; // strong
BOOL ok = [myObject tryWithError:&error];

// translated to

NSError *__strong error;
NSError *__autoreleasing tmp = error;
BOOL ok = [myObject tryWithError:&tmp];
error = tmp;

しかし、インスタンス変数を使用してそれを行う場合:

@implementation Foo {
    NSError *_error; // strong
}
- (void)bar
{
    [myObject tryWithError:&_error];
}
...

これは私たちにエラーを与えます

非ローカルオブジェクトのアドレス__autoreleasingをライトバック用のパラメータに渡します。

なぜこれが無効なのですか?コンパイラはそのようなコードを自動的にこれに変換できませんでしたか?

- (void)bar
{
    NSError *__autoreleasing tmp = _error;
    [myObject tryWithError:&tmp];
    _error = tmp;
}

結局のところ、これは私が問題を解決するためにとにかく書いているものです!

注:outキーワードをパラメーター型に追加すると、現在の値を一時変数に読み込む必要がないため、コンパイラーの作業がわずかに減少しますが、これではエラーは処理されません。

4

1 に答える 1

1

ivarへのポインタは、ARCの「id__autoreleasing *」引数に渡すことはできません。これは、そのようなパスバイライトバックの形式が正しくないためです。ARC仕様のそれぞれのセクションには、パスバイライトバックの法的形式がリストされています。ここで適用できるのは、

&var、ここでvarは、保持可能なオブジェクトを使用した自動保存期間のスカラー変数です。

したがって、自動保存期間(ローカル変数)のみが許可されます。

これが無効である理由:ここでの理由は、古いコードとの互換性であると確信しています。

1)失敗した場合のエラーライトバックのみを確認する必要があります。成功した場合、エラーポインタの内部に何があるかはまったく保証されません。

2)一般に、ライトバック値を使用するかどうかは、メソッドのコントラクトによって異なります。これはコンパイラがチェックできないものです。

&errorこれは、 (NSError * __autoreleasing *)のタイプをライトバックのタイプ(NSError **として解釈される)に一致させるコードのバージョンですNSError * __autoreleasing *。がYESの場合ok、エラー値は変更されません。

NSError * __autoreleasing error;
BOOL OK = [myObject performOperationWithError:&error];
if (!OK) {
    // use error
}

ただし、これら__autoreleasingは醜いため、コンパイラは、あちこちで使用する代わりに、 (ただしローカルの)変数(デフォルトの所有権)も__autoreleasing渡すことができます。__strong

NSError *error;
BOOL OK = [myObject performOperationWithError:&error];
if (!OK) {
    // use error
}

ドキュメントによると、それは次のように書き直されます:

NSError * __strong error;
NSError * __autoreleasing tmp = error;
BOOL OK = [myObject performOperationWithError:&tmp];
error = tmp;
if (!OK) {
    // use error
}

まったく問題ありません。エラーは成功した場合にのみ使用されます。

__strong次に、インスタンス変数を見てみましょう_error。なぜコンパイラはそれを許可しないのですか?書き換えは次のようになります。

NSError * __autoreleasing tmp = _error;
BOOL OK = [myObject performOperationWithError:&tmp];
_error = tmp;
if (!OK) {
    // use error
}

ここでの問題は、ライトバックtmp常に使用され(インスタンス変数に割り当てられ)、ライトバックはエラーの場合にのみ使用されるというメソッドのコントラクト(または一般にメソッドのドキュメントに記載されているもの_error)を無視することです。最後のエラーをインスタンス変数に割り当てる安全な方法は次のとおりです。

NSError * __autoreleasing tmp = _error;
BOOL OK = [myObject performOperationWithError:&tmp];
if (!OK) {
    _error = tmp; 
    // use error
} else {
    _error = nil; // Make sure that _error is nil if there was no error.
}

そして、それはエラーを返すCocoaのメソッドの慣習にのみ当てはまります。一般に、コンパイラがメソッドがどのように機能するかを判断する方法はid *ありません。:異なる規則を使用する古いメソッドが存在する可能性があります。したがって、本当にライトバックをインスタンス変数に格納したい場合は__strong、現在、自分でさらに1マイル歩く必要があり、これが変わることはないと思います。

于 2013-05-30T14:28:58.613 に答える