到目前为止,我一直认为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使用)定义了非常方便的_testIVar
和self->_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
的强引用。使用@strongify
和self
则不会。因此,@weakify
的重新分配不会被推迟,直到块运行。但主要的优点是您无需记住使用@strongify
或self
而不是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
。请参阅此屏幕截图:
截图的另一个有趣的事情是警告消息是
未使用的变量'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
,但事实上似乎并非如此。
答案 0 :(得分:17)
使用两个隐藏的参数(来自"Objective-C Runtime Programming Guide")调用所有Objective-C方法:
并且方法可以将接收对象称为self
(并将其自己的选择器称为_cmd
)。
现在_ivar
相当于self->_ivar
,其中self
首先隐含此
功能参数。
只要您未在内部范围中定义新变量self
,_ivar == self->_ivar
就成立。
如果您在内部范围内定义了一个新变量self
,那么您有
self
,和_ivar
仍然指的是“隐含的自我”!这解释了块中的编译器警告,它们似乎相互矛盾:
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