【发布时间】: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 的释放不会被推迟,直到块运行。但主要优点是您无需记住使用 weakSelf 或 strongSelf 代替 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 = ...会设置“新自我”的实例变量”。所以这可能是块的一个特殊问题。
标签: objective-c block reactive-cocoa ivar