最近在看Blocks Programming Topics中Objective-C Objects有如下的这段话:
When a block is copied, it creates strong references to object variables used within the block. If you use a block within the implementation of a method:
- If you access an instance variable by reference, a strong reference is made to self;
- If you access an instance variable by value, a strong reference is made to the variable.
文档中的例子给的例子比较简陋,没有说到点子上,现做如下的测试demo来验证相关结果:
类RXBlockTmpObject
:
@interface RXBlockTmpObject : NSObject
@property (nonatomic, copy) NSString *name;
+ (id)tmpObjectWithName:(NSString *)name;
@end
@implementation RXBlockTmpObject
+ (id)tmpObjectWithName:(NSString *)name
{
RXBlockTmpObject *tmp = [RXBlockTmpObject new];
tmp.name = name;
return tmp;
}
- (NSString *)description
{
// 输出内存地址,监控内存变化
return [NSString stringWithFormat:@"address:%p, name:%@", self, self.name];
}
@end
类RXBlockReferenceValueObject
文件:RXBlockReferenceValueObject.h
@interface RXBlockReferenceValueObject : NSObject
- (void)test;
@end
文件:RXBlockReferenceValueObject.m
static void _s_test_name(NSString *prefix, RXBlockTmpObject *tmpObject)
{
NSLog(@"%@, %@", prefix, tmpObject);
}
@interface RXBlockReferenceValueObject()
@property (nonatomic, strong) RXBlockTmpObject *tmpObject;
@property (nonatomic, strong) NSMutableArray *mutableArray;
@end
@implementation RXBlockReferenceValueObject
- (void)test
{
// [self _test_normal];
// [self _test_normal_mut];
// [self _test_self_instance_variable];
// [self _test_weakself_strongself_instance_variable];
}
- (void)dealloc
{
NSLog(@"RXBlockReferenceValueObject dealloc");
}
@end
在测试类中:
- (void)_test_reference_value
{
// tmp 对象没有别的地方被引用,理论上这个函数结束的时候,tmp应该会在第一时间释放
RXBlockReferenceValueObject *tmp = [RXBlockReferenceValueObject new];
[tmp test];
NSLog(@"_test_reference_value end");
}
先给文件:RXBlockReferenceValueObject.m
中添加方法:_test_normal
- (void)_test_normal
{
self.tmpObject = [RXBlockTmpObject tmpObjectWithName:@"1"];
NSLog(@"1 address in object:%@", self.tmpObject);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
self.tmpObject = [RXBlockTmpObject tmpObjectWithName:@"2"];
NSLog(@"2 address in object:%@", self.tmpObject);
// 代码1: 2秒后执行一个任务
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 2), queue, ^{
_s_test_name(@"instance variable", _tmpObject);
});
self.tmpObject = [RXBlockTmpObject tmpObjectWithName:@"3"];
NSLog(@"3 address in object:%@", self.tmpObject);
id value = _tmpObject;
NSLog(@"value address in object:%@", value);
// 代码2: 3秒后执行一个任务
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 3), queue, ^{
_s_test_name(@"local variable", value);
});
self.tmpObject = [RXBlockTmpObject tmpObjectWithName:@"4"];
NSLog(@"4 address in object:%@", self.tmpObject);
}
执行_test_normal
看得到的结果:
- 1 address in object:address:0x604000008270, name:1
- 2 address in object:address:0x604000007f10, name:2
- 3 address in object:address:0x604000008270, name:3
- value address in object:address:0x604000008270, name:3
- 4 address in object:address:0x604000007f10, name:4
- 10:16:41.718413+0800:_test_reference_value end
- 10:16:43.912842+0800:instance variable, address:0x604000007f10, name:4
- 10:16:43.913046+0800:RXBlockReferenceValueObject dealloc
- local variable, address:0x604000008270, name:3
根据输出结果,
- 前6行的结果可以很容易推导出
- 代码1的block中是
access an instance variable by reference
,所以是对self
有一个强引用,导致不能dealloc,所以输出完第7行后,然后输出第8行(注意6,7,8的时间) - 并且注意一下第7行和第5行的地址是一样的,结果也是一样的,就是会通过
self
来访问_tmpObject
(If you access an instance variable by reference, a strong reference is made to self
) - 第9行的结果跟第3行第4行的结果是一样的(
If you access an instance variable by value, a strong reference is made to the variable
)
为了验证时间,我们可以把代码1
给去掉,再次运行得到的结果如下:
- 1 address in object:address:0x600000019470, name:1
- 2 address in object:address:0x6000000194b0, name:2
- 3 address in object:address:0x600000019470, name:3
- value address in object:address:0x600000019470, name:3
- 4 address in object:address:0x60400000f2e0, name:4
- 10:32:21.664519+0800 _test_reference_value end
- 10:32:21.664583+0800 RXBlockReferenceValueObject dealloc
- 10:32:24.664712+0800 local variable, address:0x600000019470, name:3
注意这里的6,7,8行的时间结果,表明代码1
是对self
强引用了,无法第一时间调用dealloc
。
回到有代码1
的运行结果
细心的读者可以看到1和3, 2和5的地址是一样的,但是值不一样,这应该是旧的对象被释放后,新的对象申请的时候,刚好申请到被释放的内存。所以导致这样的结果,为了验证这一点,可以用如下的测试代码来验证:
- (void)_test_normal_mut
{
self.mutableArray = [NSMutableArray new];
self.tmpObject = [RXBlockTmpObject tmpObjectWithName:@"1"];
[self.mutableArray addObject:self.tmpObject];
NSLog(@"1 address in object:%@", self.tmpObject);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
self.tmpObject = [RXBlockTmpObject tmpObjectWithName:@"2"];
[self.mutableArray addObject:self.tmpObject];
NSLog(@"2 address in object:%@", self.tmpObject);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 2), queue, ^{
_s_test_name(@"instance variable", _tmpObject);
});
self.tmpObject = [RXBlockTmpObject tmpObjectWithName:@"3"];
[self.mutableArray addObject:self.tmpObject];
NSLog(@"3 address in object:%@", self.tmpObject);
id value = _tmpObject;
NSLog(@"value address in object:%@", value);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 3), queue, ^{
_s_test_name(@"local variable", value);
});
self.tmpObject = [RXBlockTmpObject tmpObjectWithName:@"4"];
[self.mutableArray addObject:self.tmpObject];
NSLog(@"4 address in object:%@", self.tmpObject);
}
输出结果:
1 address in object:address:0x60000001df10, name:1
2 address in object:address:0x6040002025a0, name:2
3 address in object:address:0x60000001df70, name:3
value address in object:address:0x60000001df70, name:3
4 address in object:address:0x6040002025b0, name:4
_test_reference_value end
instance variable, address:0x6040002025b0, name:4
RXBlockReferenceValueObject dealloc
local variable, address:0x60000001df70, name:3
这个时候可以看到4个内存地址都不一样了。
所以根据以上的结论可以得出如下的结论:
代码1
等价于如下的代码:
- (void)_test_self_instance_variable
{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
self.tmpObject = [RXBlockTmpObject tmpObjectWithName:@"4"];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 2), queue, ^{
_s_test_name(@"instance variable", self->_tmpObject);
});
}
但是不等价于(代码有编译错误):
- (void)_test_weakself_instance_variable
{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
self.tmpObject = [RXBlockTmpObject tmpObjectWithName:@"4"];
__weak typeof(self) weakSelf = self;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 2), queue, ^{
// Error: Dereferencing a __weak pointer is not allowed due to possible null value cased by race condition, assign it to strong variable first
_s_test_name(@"instance variable", weakSelf->_tmpObject);
});
}
同样不等价于:
- (void)_test_weakself_strongself_instance_variable
{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
self.tmpObject = [RXBlockTmpObject tmpObjectWithName:@"4"];
__weak typeof(self) weakSelf = self;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 2), queue, ^{
__strong typeof(self) strongSelf = weakSelf;
// 虽然不会编译错误,但是这里很大概率会崩溃
_s_test_name(@"instance variable", strongSelf->_tmpObject);
});
}
两个不等价于:
- 没有对
self
进行强引用 -
strongSelf
也有可能为空,然后直接EXC_BAD_ACCESS
网友评论