Objective-C self-gt; _ivar access with explicit vs implicit self->

时间:2013-10-29 15:00:20

标签: objective-c block reactive-cocoa ivar

一般问题

到目前为止,我一直认为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的{​​{1}},隐式ivar语法self仍会找到“原始”自我,{{1}显然不是。在后一种情况下,编译器正确地抱怨

  

成员引用类型'NSInteger'(又名'long')不是指针

然而,在第一种情况下,它才有效。

现实问题

这个例子似乎相当人为,但根本不是。例如,ExtObjC项目(由ReactiveCocoa使用)定义了非常方便的_testIVarself->_testIVar,它有助于在块中强烈捕获@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的强引用。使用@strongifyself则不会。因此,@weakify的重新分配不会被推迟,直到块运行。但主要的优点是您无需记住使用@strongifyself而不是weakSelf,因为隐藏了“原始”strongSelf

这非常方便,ExtObjC通过使用宏生成类似下面的内容来实现self / self

@weakify

很公平,这甚至更好,因为我们可以继续使用@strongify而不会实际捕获对- (void)someMethod { __weak typeof(self) _weakSelf = self; dispatch_async(self.someQueue, ^{ __strong typeof(self) self = _weakSelf; NSLog(@"self @ %p", self); } } 的强引用。但是,只要我们使用隐式ivars-of-self语法,就会捕获对“原始”self的强引用!

self

其它

在块中使用ivars时,我们肯定会捕获self。请参阅此屏幕截图: Unused and captured self

截图的另一个有趣的事情是警告消息是

  

未使用的变量'self'

并在下面一行

  

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

这就是为什么我认为- (void)someMethod { @weakify(self); dispatch_async(self.someQueue, ^{ @strongify(self); /* compiler warning: Unused variable self here!!! */ NSLog(@"self->_testIVar: %@", _testIVar); } } 有两个版本: - )

问题

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

澄清(另见我的截图):正如@MartinR指出的那样(我也是这么认为),有一些_testIVar的特殊版本无法更改,仅用于隐式自我-ivar访问。这是在某处记录的吗?基本上在哪里定义了隐式self所指的内容?它的行为似乎与Java一样(使用self),但区别在于self是您无法覆盖的保留关键字。

问题也不在于如何“修复”它,只需在this / this示例中写出self->_testIVar即可。通过使用@weakify / @strongify,我想更多的是你不能犯错误地隐含强烈地捕获@weakify,但事实上似乎并非如此。

1 个答案:

答案 0 :(得分:17)

使用两个隐藏的参数(来自"Objective-C Runtime Programming Guide")调用所有Objective-C方法:

  • 接收对象
  • 方法的选择器

并且方法可以将接收对象称为self(并将其自己的选择器称为_cmd)。

现在_ivar相当于self->_ivar,其中self首先隐含此 功能参数。 只要您未在内部范围中定义新变量self_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