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用一些NSInteger也命名self的东西隐藏了原件,隐含的 ivar 语法_testIVar仍然可以找到“原始”自我,而self->_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);
    }
}

如果没有@weakifyand @strongify,该块将捕获对 的强引用self。与它@weakify@strongify没有。self因此,在块运行之前,不会推迟释放。但是,主要优点是您不需要记住使用weakSelforstrongSelf代替,self因为“原始”self是隐藏的。

这非常方便,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);
    }
}

杂项

在块中使用 ivars 时,我们肯定会捕获self. 例如,请参阅此屏幕截图: 未使用和捕获的自我

屏幕截图的另一个有趣之处是警告消息是

未使用的变量“自我”

在下面的行中

在此块中强烈捕获“自我”可能会导致保留周期

这就是为什么我认为有两个版本的self:-)

问题

这里的实际问题是:到底是什么_testIVar意思?它如何找到“原始”self指针?

澄清(另见我的屏幕截图):正如@MartinR 指出的(这也是我的想法),有一些特殊版本self无法更改,仅用于隐式自我ivar-access。这在某处有记录吗?基本上在哪里定义了隐式self指的是什么?它的行为似乎与例如 Java 的行为相同 (with this),但不同之处this在于您无法覆盖的保留关键字。

问题也不是如何“修复”它,只是写self->_testIVar将是我在@weakify/@strongify示例中想要的。我认为通过使用@weakify/@strongify你不能再犯隐式强捕获的错误,self但事实似乎并非如此。

4

1 回答 1

20

All Objective-C methods are called with two hidden arguments (from the "Objective-C Runtime Programming Guide"):

  • The receiving object
  • The selector for the method

and a method can refer to the receiving object as self (and to its own selector as _cmd).

Now _ivar is equivalent to self->_ivar where self is this implicit first function parameter. As long as you don't define a new variable self in an inner scope, _ivar == self->_ivar holds true.

If you define a new variable self in an inner scope then you have

  • The locally defined self,
  • the "implicit self" which is the first function parameter,

and _ivar still refers to the "implicit self"! This explains the compiler warnings in your block, which seem to contradict each other:

  • "Unused variable 'self'" refers to the locally defined self,
  • "Capturing 'self' strong in this block ..." refers to the "implicit self" of the function.

The following code demonstrates also this:

@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 回答