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

Posted

技术标签:

【中文标题】Objective-C self->_ivar 访问与显式与隐式 self->【英文标题】:Objective-C self->_ivar access with explicit vs implicit self-> 【发布时间】:2013-11-08 19:40:29 【问题描述】:

一般问题

直到现在,我一直认为self->_ivar 等价于_ivar。今天我发现这并不完全正确。

例如看下面的代码sn-p:

@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

即使我用一些NSInteger 也称为self 隐藏了原始self,隐式ivar 语法_testIVar 仍然可以找到“原始”自我,而self->_testIVar 显然没有。在后一种情况下,编译器正确地抱怨

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

然而,在第一种情况下,它可以正常工作。

现实世界的问题

这个例子可能看起来很人为,但根本不是。例如 ExtObjC 项目(由 ReactiveCocoa 使用)定义了非常方便的 @weakify(var)@strongify(var) 通过定义非常方便的语法(无需写的奇怪又麻烦再写__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 的释放不会被推迟,直到块运行。但主要优点是您无需记住使用 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);
    

杂项

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

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

未使用的变量'self'

在下面一行

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

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

问题

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

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

问题也不在于如何“修复”它,在@weakify/@strongify 示例中,只需编写self->_testIVar 即可。更重要的是,我认为使用 @weakify/@strongify 您不会再犯隐式强捕获 self 的错误,但事实似乎并非如此。

【问题讨论】:

我很惊讶self 不是保留字 o_O 但是在每个- (instancetype)init 方法中,你都会使用if ((self = [super init])) ... ,不是吗?所以你分配self,因此它不能是保留关键字。 我的意思是保留,以防止您在类方法中声明具有该名称的变量。与您的问题无关,实际上只是评论。 我的猜想是_ivar 等价于self->_ivar 其中self 是每个Objective-C 方法调用所具有的隐式第一个参数,即使有同名的局部变量。我没有官方参考(否则我会写一个答案:-),但我第一次尝试阅读生成的汇编代码证实了这个猜想。 @JohannesWeiß:另一方面,self = [super init] 也会覆盖 self,在这种情况下 预期 _ivar = ... 会设置“新自我”的实例变量”。所以这可能是块的一个特殊问题。 【参考方案1】:

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

接收对象 方法的选择器

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

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

如果你在内部范围内定义一个新变量self,那么你有

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

_ivar 仍然指的是“隐式自我”!这解释了您的块中的编译器警告,这似乎相互矛盾:

“未使用的变量'self'”指的是本地定义的self"Capturing 'self' strong in this block ..."指的是函数的“隐式自我”。

以下代码也演示了这一点:

@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

【讨论】:

“所以_ivar(在实例方法中)指的是方法接收者的实例变量。”不一定是真的。如果分配给self(不是在内部范围内创建新变量;而是分配给已经存在的self变量),然后访问实例变量,它们将访问self现在指向的对象上的实例变量to,不一定与方法的接收者相同。 @newacct:是的,你是对的。稍后我会尝试改进答案。谢谢指正! @newacct:实际上,如果您分配一个新值,例如,Apple 文档中的“A 方法将接收对象称为 self”的语句也是错误的。 self = [super init]. @newacct:我已经重写了答案,欢迎您的反馈。 很高兴知道。我总是使用明确的self-> 访问,这给了我一个理由。谢谢:)

以上是关于Objective-C self->_ivar 访问与显式与隐式 self->的主要内容,如果未能解决你的问题,请参考以下文章

Objective-c:关于self = [super init]的问题

什么时候在 Objective-C 中使用`self`?

Objective-C:使用 self.navigationController 和自定义 UINavigationController

objective-c 怎么实现点击一个按钮弹出新的窗口

我可以使用 [self retain] 在objective-c 中保存对象本身吗?

IVA电脑硬件团-推荐区讯景RX6900XT到手价格8399元,比RTX3080更好的选择!