前言
我们知道 block
中使用 self.xxx
的时候,block
会持有 self
。
那么假如我们通过 _xxx
直接引用属性变量是否可以避免循环引用呢?
编写测试代码
(完整代码在最后)
- (void)startDemo
{
// 通过 self.xxx 方式访问
void (^block0)() = ^{
NSLog(@"zhangqingyu %@", self.value1);
};
block0();
// 通过 _xxx 方式访问
void (^block1)() = ^{
NSLog(@"zhangqingyu %@", _value2);
};
block1();
// 临时变量
void (^block2)() = ^{
NSString *value3 = @"Fucking block!";
NSLog(@"zhangqingyu %@", value3);
};
block2();
}
了解 Clang 命令
Clang 是一个C、C++、Objective-C和Objective-C++编程语言的编译器前端。
它采用了底层虚拟机(LLVM)作为其后端。
它的目标是提供一个GNU编译器套装(GCC)的替代品。
使用 Clang 编译上面的 Demo
clang -fobjc-arc -framework Foundation main.m -o test
执行结果
clang -fobjc-arc -framework Foundation -rewrite-objc main.m
执行结束后,可以看到多出一个 cpp 文件
cpp文件阅读
通过 self.xxx
方式访问
通过源码,我们可以找到 我们在 OC 中的 Block 被编译成以下的结构体。
结构体中可以看到一条属性QYBlockDemo *const __strong self;
这也解释了为什么当 self
持有 block
,block
引用 self
会引起循环引用。
struct __QYBlockDemo__startDemo_block_impl_0 {
struct __block_impl impl;
struct __QYBlockDemo__startDemo_block_desc_0* Desc;
QYBlockDemo *const __strong self;
__QYBlockDemo__startDemo_block_impl_0(void *fp, struct __QYBlockDemo__startDemo_block_desc_0 *desc, QYBlockDemo *const __strong _self, int flags=0) : self(_self) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __QYBlockDemo__startDemo_block_func_0(struct __QYBlockDemo__startDemo_block_impl_0 *__cself) {
QYBlockDemo *const __strong self = __cself->self; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_9w_6_s3z1fs4mg055_c5w4008pw0000gn_T_main_3c98bd_mi_2, ((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("value1")));
}
通过 _xxx
方式访问
可以看到 block
仍然持有了 self
通过这种方式并不能避免循环引用
struct __QYBlockDemo__startDemo_block_impl_1 {
struct __block_impl impl;
struct __QYBlockDemo__startDemo_block_desc_1* Desc;
QYBlockDemo *const __strong self;
__QYBlockDemo__startDemo_block_impl_1(void *fp, struct __QYBlockDemo__startDemo_block_desc_1 *desc, QYBlockDemo *const __strong _self, int flags=0) : self(_self) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
...
static void __QYBlockDemo__startDemo_block_func_1(struct __QYBlockDemo__startDemo_block_impl_1 *__cself) {
QYBlockDemo *const __strong self = __cself->self; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_9w_6_s3z1fs4mg055_c5w4008pw0000gn_T_main_3c98bd_mi_3, (*(NSString *__strong *)((char *)self + OBJC_IVAR_$_QYBlockDemo$_value2)));
}
临时变量
正常的临时变量时 不会持有 self
struct __QYBlockDemo__startDemo_block_impl_2 {
struct __block_impl impl;
struct __QYBlockDemo__startDemo_block_desc_2* Desc;
__QYBlockDemo__startDemo_block_impl_2(void *fp, struct __QYBlockDemo__startDemo_block_desc_2 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
...
static void __QYBlockDemo__startDemo_block_func_2(struct __QYBlockDemo__startDemo_block_impl_2 *__cself) {
NSString *value3 = (NSString *)&__NSConstantStringImpl__var_folders_9w_6_s3z1fs4mg055_c5w4008pw0000gn_T_main_3c98bd_mi_4;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_9w_6_s3z1fs4mg055_c5w4008pw0000gn_T_main_3c98bd_mi_5, value3);
}
结论
- 在
block
中使用self
, 会使block
持有self
- 通过下划线_xxx的方式直接访问属性变量,仍然会使
block
持有self
。
完整代码
#import <Foundation/Foundation.h>
@interface QYBlockDemo : NSObject
@property (nonatomic, strong) NSString *value1;
@property (nonatomic, strong) NSString *value2;
- (void)startDemo;
@end
@implementation QYBlockDemo
- (id)init
{
self = [super self];
if (self)
{
_value1 = @"Hello, World!";
_value2 = @"Hello, Objective-C!";
}
return self;
}
- (void)startDemo
{
// 通过 self.xxx 方式访问
void (^block0)() = ^{
NSLog(@"zhangqingyu %@", self.value1);
};
block0();
// 通过 _xxx 方式访问
void (^block1)() = ^{
NSLog(@"zhangqingyu %@", _value2);
};
block1();
// 临时变量
void (^block2)() = ^{
NSString *value3 = @"Fucking block!";
NSLog(@"zhangqingyu %@", value3);
};
block2();
}
@end
int main (int argc, const char * argv[])
{
@autoreleasepool
{
QYBlockDemo *demo = [[QYBlockDemo alloc] init];
[demo startDemo];
}
return 0;
}