4

質問のトピックからの変数宣言が正当であるかどうか疑問に思っています。次のコードを想像してください。

__weak typeof(self) weakSelf = self;
[self doSomethingThatMayCauseRetainCycleWithBlock:^{
    typeof(self) self = weakSelf; // <---- !!!!
    if (self == nil) return;

    NSAssert(self.someProperty != nil, @"This doesn't lead to retain cycle!");

    [self doSomething];
    self.someProperty = someValue;

    // even
    self->someIvar = anotherValue;
}

このコードは Xcode 4.5.2 で完全に機能し、Declaration shadows a local variable.

この癖のポイントは何ですか:

  1. 弱い変数への強い参照として再宣言selfすると、ブロックの内部/外部にコードを安全にコピー/移動できますが、保持サイクルが発生するリスクはありません (ivar を除きますが、それらは悪です)。
  2. NSAssertブロック内でリテイン サイクルが発生しなくなりました。

更新この手法がマクロに使用さlibextobjcれ ていることを発見しました。@weakify/@strongify

4

2 に答える 2

5

この回答には 2 つの部分があります。

  1. Declarationの根本的な原因は、ローカル変数の警告を覆い隠しGCC_WARN_SHADOWています。
  2. それに代わるものを構築するのに、NSAssertこの策略は必要ありません

GCC_WARN_SHADOW

あなたが得ている警告GCC_WARN_SHADOWは、より役に立たない警告の1つです(とにかく、私の意見では)。コンパイラはここで正しいことを行っていますが、そのおかげで、意図した以外のことをしている可能性GCC_WARN_SHADOWがあることに注意を向けています。

これは、より妄想的なコンパイラ警告が得意とする類のものです。欠点は、自分が何をしているのかを知っていることを示す特定の警告を削除するのが難しい (不可能ではない) ことです。

on の場合、このGCC_WARN_SHADOWコードは警告を生成します。

int value = MAX(1,MAX(2,3));

それにもかかわらず、それは完全に機能します。

これは醜いですが (コンパイラにとっては) 完全に明らかです。

({
   int __a = (1);
   int __b = (({
       int __a = (2);
       int __b = (3);
       __a < __b ? __b : __a;
   }));
   __a < __b ? __b : __a;
})

だからあなたが提案していることはうまくいくでしょう。NSAssert変数を使用するマクロを使用して実装されselfます。selfスコープで定義すると、self代わりにそれが取得されます。

NSAssert の置き換え

とはいえ、用途があると思われる場合GCC_WARN_SHADOWは、別の解決策があります。いずれにせよ良いかもしれません: 独自のマクロを用意してください。

#define BlockAssert(condition, desc, ...) \
    do { \
        __PRAGMA_PUSH_NO_EXTRA_ARG_WARNINGS \
        if (!(condition)) { \
            [[NSAssertionHandler currentHandler] handleFailureInMethod:_cmd \
            object:strongSelf file:[NSString stringWithUTF8String:__FILE__] \
                lineNumber:__LINE__ description:(desc), ##__VA_ARGS__]; \
        } \
        __PRAGMA_POP_NO_EXTRA_ARG_WARNINGS \
    } while(0)

これは本質的に のコピー アンド ペーストですが、代わりに をNSAssert使用しています。patternを使用する場所では、機能します。が定義されていない場所では、コンパイラ エラー: Use of undeclared identifier: 'strongSelf' が発生します。かなり良いヒントだと思います。strongSelfselfstrongSelfstrongSelf

于 2013-02-21T22:10:30.240 に答える
3

厳密に言えば、コードに問題はありません。変数宣言は正当です。ただし、ローカル変数がインスタンス 1 をシャドウしているというコンパイラ警告が表示される可能性があります。

GCD ブロックは実際には C 関数であり、Objective-C メソッドではありません。コンパイルされると、すべての Objective-C インスタンス メソッドに追加のパラメーター (selfポインター) が追加されます。self他の変数のようにオブジェクト構造体に格納されません。

このため、共有しようとしているライブラリでこのコードを使用することを躊躇します。実際には、ランタイムをすぐに明らかにするよりも少しハッキングしているため、コンパイラの新しいバージョンではコードが壊れる可能性があります。さらに、あなたが指摘するように、それは風変わりなコードです:)他の誰かがそれを読んで何が起こっているのかをすぐに理解できるかどうかはわかりません。

于 2013-01-07T13:09:04.730 に答える