23

一般的な問題

self->_ivar今までは、は に等しいと思っていました_ivar。今日、これが完全に真実ではないことがわかりました。

たとえば、次のコード スニペットを参照してください。

@interface TestClass : NSObject {
    NSString *_testIVar;
}
@end

@implementation TestClass

- (instancetype)init
{
    if ((self = [super init])) {
        _testIVar = @"Testing Only";
    }
    return self;
}

- (void)test
{
    {
        NSInteger self = 42;
        NSLog(@"without arrow: %@", _testIVar);        /* OK              */
        NSLog(@"with    arrow: %@", self->_testIVar);  /* COMPILER ERROR! */
    }
}

@end

という名前のself一部でオリジナルを隠したとしても、暗黙のivar構文は依然として「オリジナルの」自己を見つけますが、明らかにそうではありません。後者の場合、コンパイラは正しく不平を言いますNSIntegerself_testIVarself->_testIVar

メンバー参照型 'NSInteger' (別名 'long') はポインターではありません

ただし、最初のケースでは、それは機能します。

現実世界の問題

この例はかなり人工的に見えるかもしれませんが、まったくそうではありません。たとえば、ExtObjCプロジェクト ( ReactiveCocoaで使用) は、非常に便利な構文を定義することで、ブロック内 (@weakify(var)および他のオブジェクト)@strongify(var)を強くキャプチャするのを防ぐのに役立つ非常に便利selfな構文を定義しています (奇妙で面倒な構文を記述する必要は__weak typeof(self) weakSelf = self; [...] ^{ __strong typeof(self) strongSelf = weakSelf; [...] } もうありません)。例えば:

- (void)someMethod
{
    @weakify(self);
    dispatch_async(self.someQueue, ^{
        @strongify(self);
        NSLog(@"self @ %p", self);
    }
}

とがない@weakify@strongify、ブロックは への強い参照をキャプチャしselfます。で@weakify@strongifyそうではありません。したがって、selfブロックが実行されるまで、の割り当て解除は延期されません。ただし、主な利点は、「オリジナル」が隠されているため、 weakSelforstrongSelfの代わりに使用することを覚えておく必要がないことです。selfself

これは非常に便利です。ExtObjC は、マクロを使用して次のようなものを生成することで@weakify/を実装します。@strongify

- (void)someMethod
{
    __weak typeof(self) _weakSelf = self;
    dispatch_async(self.someQueue, ^{
        __strong typeof(self) self = _weakSelf;
        NSLog(@"self @ %p", self);
    }
}

selfへの強い参照を実際に取得せずに使い続けることができるので、それはさらに良いことselfです。しかし、implicit-ivars-of-self-syntax を使用するとすぐに、「オリジナル」への強い参照selfが引き続きキャプチャされます。

- (void)someMethod
{
    @weakify(self);
    dispatch_async(self.someQueue, ^{
        @strongify(self);  /* compiler warning: Unused variable self here!!! */
        NSLog(@"self->_testIVar: %@", _testIVar);
    }
}

その他

ブロックで ivar を使用する場合、確実に をキャプチャしていますself。たとえば、次のスクリーンショットを参照してください 未使用およびキャプチャされた自己

スクリーンショットのもう 1 つの興味深い点は、警告メッセージが

未使用の変数「自己」

そして下の行で

このブロックで「自己」を強くキャプチャすると、保持サイクルが発生する可能性があります

そのため、2つのバージョンがあると思いますself:-)

質問

ここでの実際の質問は次のとおりです。正確には_testIVarどういう意味ですか? 「元の」selfポインタをどのように見つけるのですか?

明確にするために(私のスクリーンショットも参照してください):@MartinRが指摘したように(これは私もそう思います)、self変更できず、暗黙的な自己ivarアクセス​​にのみ使用される特別なバージョンがあります。それはどこかに文書化されていますか?基本的に、暗黙の参照先はどこで定義されていselfますか? たとえば Java と同じように動作するように見えますが ( を使用)、オーバーライドできない予約済みキーワードであるというthis違いがあります。this

問題は、それを「修正」する方法でもありません。 /の例self->_testIVarで私が望むのは、ただ書くだけです。/を使用することで、暗黙的に強力にキャプチャするという間違いを犯すことはできなくなりましたが、そうではないようです。@weakify@strongify@weakify@strongifyself

4

1 に答える 1

20

すべての Objective-C メソッドは、2 つの隠し引数を使用して呼び出されます ( 「Objective-C ランタイム プログラミング ガイド」より)。

  • 受信オブジェクト
  • メソッドのセレクター

また、メソッドは受信オブジェクトを として参照できますself(およびそれ自体のセレクターを として参照できます_cmd)。

Nowは、この暗黙的な最初の関数パラメーターである where is_ivarと同等です。内部スコープで新しい変数を定義しない限り、true が保持されます。self->_ivarselfself_ivar == self->_ivar

self内部スコープで新しい変数を定義すると、

  • ローカルで定義されselfた、
  • 最初の関数パラメーターである「暗黙の自己」、

それでも「暗黙の自己」を_ivar指します!これは、互いに矛盾しているように見える、ブロック内のコンパイラの警告を説明しています。

  • 「未使用の変数 'self'」は、ローカルで定義されたself
  • 「このブロックで強力な「自己」をキャプチャする...」は、関数の「暗黙の自己」を指します。

次のコードもこれを示しています。

@interface MyClass : NSObject
{
    NSString *_ivar;
}
@end

@implementation MyClass

- (void)test
{
    _ivar = @"foo"; // Set instance variable of receiver
    {
        MyClass *self = [MyClass new]; // Redefine self in inner scope
        self->_ivar = @"bar"; // Set instance variable of redefined self
        NSLog(@"%@ - %@", self->_ivar, _ivar);
        // Output: bar - foo
    }
}

@end
于 2013-10-29T18:06:54.923 に答える