Objective-C self - > _ ivar access with explicit vs implicit self->

Joh*_*iss 20 block objective-c ivar reactive-cocoa

一般问题

直到现在,我一直认为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
Run Code Online (Sandbox Code Playgroud)

即使我隐藏了原始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);
    }
}
Run Code Online (Sandbox Code Playgroud)

没有@weakify@strongify,该块将捕获一个强引用self.随着@weakify@strongify它却没有.因此,self在块运行之前,不会推迟释放.但主要的优点是你不需要记住使用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);
    }
}
Run Code Online (Sandbox Code Playgroud)

很公平,这甚至更好,因为我们可以继续使用self而不会实际捕获强引用self.但是,只要我们使用隐式ivars-of-self语法,self就会捕获对"原始"的强引用!

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

杂项

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

关于屏幕截图的另一个有趣的事情是警告消息

未使用的变量'self'

并在下面的行

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

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

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

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

问题也不是如何"修复"它,只是self->_testIVar@weakify/ @strongify示例中写出我想要的东西.更多的是我通过使用@weakify/ @strongify你不能犯错误的隐含强烈捕获self,但事实上似乎并非如此.

Mar*_*n R 17

所有Objective-C方法都使用两个隐藏参数调用(来自"Objective-C运行时编程指南"):

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

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

现在_ivar相当于这个隐含的第一个函数参数self->_ivar在哪里.只要您没有在内部范围中定义新变量,就保持为true.selfself_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
Run Code Online (Sandbox Code Playgroud)