Objective-C self->_ivar 访问显式与隐式 self->

标签 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

即使我用一些也命名为 selfNSInteger 隐藏了原始的 self,隐式的 ivar 语法 _testIVar仍然找到“原始” self ,而 self->_testIVar 显然没有。在后一种情况下,编译器正确地提示

Member reference type 'NSInteger' (aka 'long') is not a pointer

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

现实世界的问题

这个例子可能看起来很不自然,但事实并非如此。例如 ExtObjC项目(由 ReactiveCocoa 使用)定义了非常方便的 @weakify(var)@strongify(var) 这有助于防止强烈捕获 self (和其他对象)在 block 中定义一个非常方便的语法(不需要编写奇怪和繁琐的代码来编写 __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,该 block 将捕获对 self 的强引用。使用 @weakify@strongify 则不会。所以 self 的释放不会被推迟,直到 block 已经运行。不过,主要优点是您不需要记住使用 weakSelfstrongSelf 而不是 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);
    }
}

杂项

在 block 中使用 ivars 时,我们肯定会捕获 self。例如,请参见此屏幕截图: Unused and captured self .

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

Unused variable 'self'

在下面一行

Capturing 'self' strongly in this block is likely to lead to a retain cycle

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

问题

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

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

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

最佳答案

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

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

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

现在 _ivar 等同于 self->_ivar 其中 self隐式优先 函数参数。 只要您不在内部范围内定义新变量 self_ivar == self->_ivar 就成立。

如果您在内部作用域中定义了一个新变量self,那么您就有了

  • 本地定义的self,
  • 作为第一个函数参数的“隐式 self ”,

_ivar仍然指的是“内隐 self ”!这解释了你的 block 中的编译器警告,它们似乎相互矛盾:

  • “Unused variable 'self'”指的是本地定义的self,
  • “在此 block 中捕获‘ 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

关于Objective-C self->_ivar 访问显式与隐式 self->,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19661808/

相关文章:

swift - react 性 Cocoa MVVM 初始化

ios - ReactiveCocoa 条件异步信号

Objective-C - 以编程方式确定 iPod touch 的 IP 地址

ios - 无法将文件的所有者连接到 View

java - Java 中的 block 语句

java - 阻止 java 热键,例如 alt+esc、control-alt-delete

objective-c - 拆分 RACSignal 以消除状态

objective-c - UIButton addTarget 方法...不起作用?

ios - Firebase 后台和非事件通知不显示 iOS

HTML/CSS 马赛克图片库