一、概述
Block是C级别的语法和运行时特性。Block比较类似C函数,但是Block比之C函数,其灵活性体现在栈内存、堆内存的引用,我们甚至可以将一个Block作为参数传给其他的函数或者Block
二、本质
为了探究Block的本质,新建一个Command Line Tool 工程,声明一个简单的Block。
#import
int main(int argc, const char * argv[]) {
@autoreleasepool {
void (^JDPBlock) (void) = ^{printf("Hello JDPBlock");};
}
return 0;
}
cd 到源文件目录,rewrite
clang -rewrite-objc main.m
得到文件main.cpp
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
printf("Hello JDPBlock");}
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
void (*JDPBlock) (void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
}
return 0;
}
转换过程见此篇,不赘述
三、MRC
在MRC下根据存放位置的不同,Block分为三类:NSGlobalBlock、NSStackBlock、NSMallocBlock
1、NSGlobalBlock
没有访问栈变量的Block都为NSGlobalBlock
输出字符串常量:
- (void)testGlobalBlock {
void (^GlobalBlock)(void) = ^() {
NSLog(@"Hello Block");
};
NSLog(@"%@", GlobalBlock);
GlobalBlock();
}
2019-12-20 10:28:29.663904+0800 BlockBlog[1297:57162] <__NSGlobalBlock__: 0x1031cf070>
2019-12-20 10:28:31.768400+0800 BlockBlog[1297:57162] Hello Block
输出全局变量:
NSInteger GlobalVar = 1;
- (void)testGlobalBlock {
void (^GlobalBlock)(void) = ^() {
NSLog(@"%ld", GlobalVar);
};
NSLog(@"%@", GlobalBlock);
GlobalBlock();
}
2019-12-20 10:31:39.861782+0800 BlockBlog[1435:68044] <__NSGlobalBlock__: 0x101010070>
2019-12-20 10:31:41.976316+0800 BlockBlog[1435:68044] 1
2、NSStackBlock
访问栈变量的Block为NSStackBlock
输出局部变量:
- (void)testStackBlock {
NSInteger stackVar = 2;
void (^StackBlock)(void) = ^() {
NSLog(@"%ld", stackVar);
};
NSLog(@"%@", StackBlock);
StackBlock();
}
2019-12-20 10:58:13.224019+0800 BlockBlog[2153:130901] <__NSStackBlock__: 0x7ffee6bb2088>
2019-12-20 10:58:16.148405+0800 BlockBlog[2153:130901] 2
2.1、NSStackBlock访问基本数据类型
修饰符__block对block内部基本类型变量的影响
2.1.1、不加__block修饰符
- (void)testStackBlock {
NSInteger stackVar = 2;
NSLog(@"stackVar address:%p", &stackVar);
void (^StackBlock)(void) = ^() {
NSLog(@"stackVar(Block) address:%p", &stackVar);
};
StackBlock();
NSLog(@"%@", StackBlock);
}
2019-12-20 11:04:11.002604+0800 BlockBlog[2411:152313] stackVar address:0x7ffee4f770b8
2019-12-20 11:04:11.002848+0800 BlockBlog[2411:152313] stackVar(Block) address:0x7ffee4f770a8
2019-12-20 11:04:11.002949+0800 BlockBlog[2411:152313] <__NSStackBlock__: 0x7ffee4f77088>
block对不加__block修饰符的基本数据类型变量进行了拷贝,内外访问不是一块内存地址。和C语言基本数据类型做函数形参属于简单的值传递有些相似。
1、block被转换为结构体类型,并增加了一个截获变量类型的成员变量。
2、block定义时,调用了block结构体的构造函数,对基本数据类型变量进行了函数形参的值传递,保存到block结构体实例的成员变量中。
3、block定义后,局部变量值的修改不会影响到block结构体实例的成员变量的值
4、block执行时,访问截获的变量其实是访问的block结构体实例的成员变量。
2.1.2、添加__block修饰符
- (void)testStackBlock {
__block NSInteger stackVar = 2;
NSLog(@"stackVar address:%p", &stackVar);
void (^StackBlock)(void) = ^() {
NSLog(@"stackVar(Block) address:%p", &stackVar);
};
StackBlock();
NSLog(@"%@", StackBlock);
}
2019-12-20 11:35:48.956734+0800 BlockBlog[3232:209066] stackVar address:0x7ffee03600b8
2019-12-20 11:35:54.761303+0800 BlockBlog[3232:209066] stackVar(Block) address:0x7ffee03600b8
2019-12-20 11:35:59.755943+0800 BlockBlog[3232:209066] <__NSStackBlock__: 0x7ffee0360060>
对添加__block修饰符的变量,其地址会被拷贝置前述的内存区中,内外访问的是一块内存地址,与C语言的指针类型作函数参数相似。
1、添加了__block修饰符的变量,将被转换成结构体类型。
2、在__block变量定义时,调用了结构体的构造函数,将值保存在了结构体实例的成员变量中
3、在其后的block结构体定义和执行时,都是访问的__block结构体实例的成员变量。
2.1.3、添加__block修饰符验证
- (void)testStackBlock {
__block NSInteger stackVar = 2;
NSLog(@"stackVar:%ld", stackVar);
NSLog(@"stackVar address:%p", &stackVar);
void (^StackBlock)(void) = ^() {
stackVar++;
NSLog(@"stackVar(Block):%ld", stackVar);
NSLog(@"stackVar(Block) address:%p", &stackVar);
};
StackBlock();
stackVar++;
NSLog(@"stackVar:%ld", stackVar);
NSLog(@"stackVar address:%p", &stackVar);
NSLog(@"%@", StackBlock);
}
2019-12-20 11:49:13.427384+0800 BlockBlog[3604:239956] stackVar:2
2019-12-20 11:49:13.856289+0800 BlockBlog[3604:239956] stackVar address:0x7ffeec6bf0b8
2019-12-20 11:49:15.723084+0800 BlockBlog[3604:239956] stackVar(Block):3
2019-12-20 11:49:16.217291+0800 BlockBlog[3604:239956] stackVar(Block) address:0x7ffeec6bf0b8
2019-12-20 11:49:18.984754+0800 BlockBlog[3604:239956] stackVar:4
2019-12-20 11:49:19.829138+0800 BlockBlog[3604:239956] stackVar address:0x7ffeec6bf0b8
2019-12-20 11:49:20.874982+0800 BlockBlog[3604:239956] <__NSStackBlock__: 0x7ffeec6bf060>
2.2、NSStackBlock访问对象类型
因为对象类型需要研究的点较多,预定义宏:
#define MyLog(Comment, Obj) NSLog(@"注释:%@_指针地址:%p_对象地址:%p_对象:%@_引用计数:%ld", Comment, &Obj, Obj, Obj, [Obj retainCount]);
2.2.1、不加__block修饰符
- (void)testStackBlock {
NSObject *object = [[NSObject alloc] init];
MyLog(@"Object", object);
void (^StackBlock)(void) = ^() {
MyLog(@"Block Object", object);
};
StackBlock();
MyLog(@"StackBlock", StackBlock);
[object release];
}
2019-12-20 12:05:43.619042+0800 BlockBlog[4079:270166] 注释:Object_指针地址:0x7ffee48060b8_对象地址:0x60000097a780_对象:_引用计数:1
2019-12-20 12:05:43.619267+0800 BlockBlog[4079:270166] 注释:Block Object_指针地址:0x7ffee48060a8_对象地址:0x60000097a780_对象:_引用计数:1
2019-12-20 12:05:43.619366+0800 BlockBlog[4079:270166] 注释:StackBlock_指针地址:0x7ffee48060b0_对象地址:0x7ffee4806088_对象:<__NSStackBlock__: 0x7ffee4806088>_引用计数:1
block对不加__block修饰符的对象类型变量增加了一个指针指向,但并没有增加对象的引用计数。
block结构体类型新增一个NSObject类型的成员变量,定义时,调用构造函数传递指针类型参数,访问的是同一个内存地址。
2.1.2、添加__block修饰符
- (void)testStackBlock {
__block NSObject *object = [[NSObject alloc] init];
MyLog(@"Object", object);
void (^StackBlock)(void) = ^() {
MyLog(@"Block Object", object);
};
StackBlock();
MyLog(@"StackBlock", StackBlock);
[object release];
}
2019-12-20 12:17:34.652867+0800 BlockBlog[4391:290717] 注释:Object_指针地址:0x7ffeea1a20b8_对象地址:0x60000237c270_对象:_引用计数:1
2019-12-20 12:17:35.762576+0800 BlockBlog[4391:290717] 注释:Block Object_指针地址:0x7ffeea1a20b8_对象地址:0x60000237c270_对象:_引用计数:1
2019-12-20 12:17:38.476372+0800 BlockBlog[4391:290717] 注释:StackBlock_指针地址:0x7ffeea1a2078_对象地址:0x7ffeea1a2050_对象:<__NSStackBlock__: 0x7ffeea1a2050>_引用计数:1
对添加__block修饰符的变量,内外使用的是一个指针。
1、添加了__block修饰符的变量,被转换成了结构体类型,并包含一个NSObject类型的成员变量,调用构造函数时,将引用保存到了成员变量中。
2、在block结构体实例内、外访问的__block结构体实例是同一个,其成员变量的引用也相同。
3、NSMallocBlock
对NSStackBlock执行Block_copy()或者copy方法,将会得到一个存在heap的block的引用。注:原来的NSStackBlock在作用域内依然存在。
3.1、NSMallocBlock访问基本数据类型
- (void)testMallocBlock {
NSInteger stackVar = 2;
NSLog(@"stackVar:%ld", stackVar);
NSLog(@"stackVar address:%p", &stackVar);
void (^StackBlock)(void) = ^() {
NSLog(@"stackVar(Block):%ld", stackVar);
NSLog(@"stackVar(Block) address:%p", &stackVar);
};
StackBlock();
MyLog(@"StackBlock", StackBlock);
void (^MallocBlock)(void) = Block_copy(StackBlock);
MyLog(@"StackBlock", StackBlock);
MyLog(@"MallocBlock", MallocBlock);
MallocBlock();
NSLog(@"stackVar:%ld", stackVar);
NSLog(@"stackVar address:%p", &stackVar);
}
2019-12-22 15:08:08.973506+0800 BlockBlog[4841:221535] stackVar:2
2019-12-22 15:08:08.973714+0800 BlockBlog[4841:221535] stackVar address:0x7ffeee82f0b8
2019-12-22 15:08:10.378907+0800 BlockBlog[4841:221535] stackVar(Block):2
2019-12-22 15:08:10.379114+0800 BlockBlog[4841:221535] stackVar(Block) address:0x7ffeee82f0a8
2019-12-22 15:08:10.379275+0800 BlockBlog[4841:221535] 注释:StackBlock_指针地址:0x7ffeee82f0b0_对象地址:0x7ffeee82f088_对象:<__NSStackBlock__: 0x7ffeee82f088>_引用计数:1
2019-12-22 15:08:10.379379+0800 BlockBlog[4841:221535] 注释:StackBlock_指针地址:0x7ffeee82f0b0_对象地址:0x7ffeee82f088_对象:<__NSStackBlock__: 0x7ffeee82f088>_引用计数:1
2019-12-22 15:08:10.379473+0800 BlockBlog[4841:221535] 注释:MallocBlock_指针地址:0x7ffeee82f080_对象地址:0x600003674570_对象:<__NSMallocBlock__: 0x600003674570>_引用计数:1
2019-12-22 15:08:12.258836+0800 BlockBlog[4841:221535] stackVar(Block):2
2019-12-22 15:08:12.258965+0800 BlockBlog[4841:221535] stackVar(Block) address:0x600003674590
2019-12-22 15:08:12.259034+0800 BlockBlog[4841:221535] stackVar:2
2019-12-22 15:08:12.259105+0800 BlockBlog[4841:221535] stackVar address:0x7ffeee82f0b8
3.2、NSMallocBlock访问对象类型
3.2.1、不加__block修饰符
- (void)testMallocBlock {
NSObject *object = [[NSObject alloc] init];
MyLog(@"Object", object);
void (^StackBlock)(void) = ^() {
MyLog(@"Block Object", object);
};
MyLog(@"StackBlock", StackBlock);
StackBlock();
void (^MallocBlock)(void) = Block_copy(StackBlock);
MyLog(@"Object", object);
MyLog(@"MallocBlock", MallocBlock);
MallocBlock();
Block_release(MallocBlock);
MyLog(@"Object", object);
[object release];
}
2019-12-22 15:16:07.725862+0800 BlockBlog[5066:245483] 注释:Object_指针地址:0x7ffeeefd30b8_对象地址:0x6000016065d0_对象:_引用计数:1
2019-12-22 15:16:08.896491+0800 BlockBlog[5066:245483] 注释:StackBlock_指针地址:0x7ffeeefd30b0_对象地址:0x7ffeeefd3088_对象:<__NSStackBlock__: 0x7ffeeefd3088>_引用计数:1
2019-12-22 15:16:09.863981+0800 BlockBlog[5066:245483] 注释:Block Object_指针地址:0x7ffeeefd30a8_对象地址:0x6000016065d0_对象:_引用计数:1
2019-12-22 15:16:11.224562+0800 BlockBlog[5066:245483] 注释:Object_指针地址:0x7ffeeefd30b8_对象地址:0x6000016065d0_对象:_引用计数:2
2019-12-22 15:16:11.719379+0800 BlockBlog[5066:245483] 注释:MallocBlock_指针地址:0x7ffeeefd3080_对象地址:0x600001a5f9c0_对象:<__NSMallocBlock__: 0x600001a5f9c0>_引用计数:1
2019-12-22 15:16:12.811445+0800 BlockBlog[5066:245483] 注释:Block Object_指针地址:0x600001a5f9e0_对象地址:0x6000016065d0_对象:_引用计数:2
2019-12-22 15:16:27.617261+0800 BlockBlog[5066:245483] 注释:Object_指针地址:0x7ffeeefd30b8_对象地址:0x6000016065d0_对象:_引用计数:1
对block执行Block_copy后,不加__block修饰符的对象类型变量的引用计数增加为2,对block执行Block_release后,对象的引用计数又减为1。
3.2.1、添加__block修饰符
- (void)testMallocBlock {
__block NSObject *object = [[NSObject alloc] init];
MyLog(@"Object", object);
void (^StackBlock)(void) = ^() {
MyLog(@"Block Object", object);
};
MyLog(@"StackBlock", StackBlock);
StackBlock();
void (^MallocBlock)(void) = Block_copy(StackBlock);
MyLog(@"Object", object);
MyLog(@"MallocBlock", MallocBlock);
MallocBlock();
Block_release(MallocBlock);
MyLog(@"Object", object);
[object release];
}
2019-12-22 15:18:17.232005+0800 BlockBlog[5137:251392] 注释:Object_指针地址:0x7ffee48da0b8_对象地址:0x6000017381e0_对象:_引用计数:1
2019-12-22 15:18:22.586181+0800 BlockBlog[5137:251392] 注释:StackBlock_指针地址:0x7ffee48da078_对象地址:0x7ffee48da050_对象:<__NSStackBlock__: 0x7ffee48da050>_引用计数:1
2019-12-22 15:18:26.264866+0800 BlockBlog[5137:251392] 注释:Block Object_指针地址:0x7ffee48da0b8_对象地址:0x6000017381e0_对象:_引用计数:1
2019-12-22 15:18:48.024314+0800 BlockBlog[5137:251392] 注释:Object_指针地址:0x600001b7e638_对象地址:0x6000017381e0_对象:_引用计数:1
2019-12-22 15:19:07.104200+0800 BlockBlog[5137:251392] 注释:MallocBlock_指针地址:0x7ffee48da048_对象地址:0x600001b7e3a0_对象:<__NSMallocBlock__: 0x600001b7e3a0>_引用计数:1
2019-12-22 15:19:09.545584+0800 BlockBlog[5137:251392] 注释:Block Object_指针地址:0x600001b7e638_对象地址:0x6000017381e0_对象:_引用计数:1
2019-12-22 15:19:28.345517+0800 BlockBlog[5137:251392] 注释:Object_指针地址:0x600001b7e638_对象地址:0x6000017381e0_对象:_引用计数:1
对添加了__block修饰符的对象类型变量,对block执行Block_copy后指针转移到了heap中,Block_copy和 Block_release对对象的引用计数没有影响。
3.3、MRC下的循环引用问题
@property (nonatomic, copy) void (^iVarBlock) (void);
- (void)testCycleReference {
MyLog(@"self", self);
self.iVarBlock = ^{
// self
MyLog(@"self", self);
// ivar
// MyLog(@"self", _title);
// method
// MyLog(@"self", [self description]);
};
MyLog(@"Block", _iVarBlock);
}
2019-12-22 16:45:26.626182+0800 BlockBlog[7592:349282] 注释:self_指针地址:0x7ffee076a0c8_对象地址:0x7ff5add35ff0_对象:_引用计数:3
2019-12-22 16:45:26.626392+0800 BlockBlog[7592:349282] 注释:Block_指针地址:0x7ff5add36340_对象地址:0x600000b190e0_对象:<__NSMallocBlock__: 0x600000b190e0>_引用计数:1
在block中直接访问self、访问self的成员变量和成员方法,都会导致self的引用计数加1。
如果self又同时引用了block,则会导致循环引用。
解决方法:为self添加一个__block修饰符
- (void)testCycleReference {
__block typeof(self) blockSelf = self;
MyLog(@"self", blockSelf);
self.iVarBlock = ^{
// self
MyLog(@"self", blockSelf);
// ivar
// MyLog(@"self", _title);
// method
// MyLog(@"self", [self description]);
};
MyLog(@"Block", _iVarBlock);
}
四、ARC
在ARC下,Block同样分为三类:NSGlobalBlock、NSStackBlock、NSMallocBlocks
修改宏定义:
#if __has_feature(objc_arc)
#define MyLog(Comment, Obj) NSLog(@"注释:%@_指针地址:%p_对象地址:%p_对象:%@", Comment, &Obj, Obj, Obj);
#else
#define MyLog(Comment, Obj) NSLog(@"注释:%@_指针地址:%p_对象地址:%p_对象:%@_引用计数:%ld", Comment, &Obj, Obj, Obj, [Obj retainCount]);
#endif
1、NSGlobalBlock
与MRC没有什么差异。
2、NSStackBlock
2.1、Block类型作为属性被assign修饰时
@property (nonatomic, assign) void (^iVarBlock) (void);
- (void)testStackBlock {
NSInteger stackVar = 2;
self.iVarBlock = ^{
NSLog(@"stackVar:%ld", stackVar);
};
MyLog(@"Block", _iVarBlock);
_iVarBlock();
}
2019-12-22 17:22:38.912053+0800 BlockBlog[8668:419567] 注释:Block_指针地址:0x7ffde6c0e380_对象地址:0x7ffeebc81080_对象:<__NSStackBlock__: 0x7ffeebc81080>
2.2、Block类型作为函数参数时
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
NSInteger stackVar = 2;
[self testStackBlock:^{
NSLog(@"stackVar:%ld", stackVar);
}];
}
- (void)testStackBlock:(void (^) (void))stackBlock {
MyLog(@"Block", stackBlock);
stackBlock();
}
2019-12-22 17:36:07.119089+0800 BlockBlog[9102:457220] 注释:Block_指针地址:0x7ffee1207078_对象地址:0x7ffee12070b0_对象:<__NSStackBlock__: 0x7ffee12070b0>
2019-12-22 17:36:10.021307+0800 BlockBlog[9102:457220] stackVar:2
3、NSMallocBlock
3.1、NSMallocBlock访问基本数据类型
3.1.1、不加__block修饰符
- (void)testMallocBlock {
NSInteger stack_var = 1;
NSLog(@"stack_var:%ld stack_var Address:%p", stack_var, &stack_var);
void (^MallocBlock)(void) = ^() {
NSLog(@"stack_var:%ld stack_var Address:%p", stack_var, &stack_var);
};
MyLog(@"MallocBlock", MallocBlock);
MallocBlock();
NSLog(@"stack_var:%ld stack_var Address:%p", stack_var, &stack_var);
stack_var ++;
NSLog(@"stack_var:%ld stack_var Address:%p", stack_var, &stack_var);
MallocBlock();
}
2019-12-22 17:43:18.607711+0800 BlockBlog[9319:471777] stack_var:1 stack_var Address:0x7ffee86fd0a8
2019-12-22 17:43:26.336411+0800 BlockBlog[9319:471777] 注释:MallocBlock_指针地址:0x7ffee86fd0a0_对象地址:0x6000026796b0_对象:<__NSMallocBlock__: 0x6000026796b0>
2019-12-22 17:43:33.391549+0800 BlockBlog[9319:471777] stack_var:1 stack_var Address:0x6000026796d0
2019-12-22 17:44:07.861631+0800 BlockBlog[9319:471777] stack_var:1 stack_var Address:0x7ffee86fd0a8
2019-12-22 17:44:11.585388+0800 BlockBlog[9319:471777] stack_var:2 stack_var Address:0x7ffee86fd0a8
2019-12-22 17:44:16.120562+0800 BlockBlog[9319:471777] stack_var:1 stack_var Address:0x6000026796d0
3.2、NSMallocBlock访问对象类型
3.2.1、添加__block修饰符
- (void)testMallocBlock {
NSObject *object = [[NSObject alloc] init];
MyLog(@"Object", object);
__block NSObject *blockObject = object;
MyLog(@"blockObject", blockObject);
void (^MallocBlock)(void) = ^ {
MyLog(@"Block blockObj", blockObject);
};
object = nil;
MyLog(@"Object", object);
MallocBlock();
MyLog(@"%@", MallocBlock);
}
2019-12-22 18:10:19.713250+0800 BlockBlog[10140:528772] 注释:Object_指针地址:0x7ffeef24d0a8_对象地址:0x600000714f70_对象:
2019-12-22 18:10:19.713507+0800 BlockBlog[10140:528772] 注释:blockObject_指针地址:0x7ffeef24d0a0_对象地址:0x600000714f70_对象:
2019-12-22 18:10:19.713592+0800 BlockBlog[10140:528772] 注释: Object_指针地址:0x7ffeef24d0a8_对象地址:0x0_对象:(null)
2019-12-22 18:10:19.713707+0800 BlockBlog[10140:528772] 注释:Block blockObj_指针地址:0x600000b48e38_对象地址:0x600000714f70_对象:
2019-12-22 18:10:19.713794+0800 BlockBlog[10140:528772] 注释:%@_指针地址:0x7ffeef24d060_对象地址:0x600000b48c30_对象:<__NSMallocBlock__: 0x600000b48c30>
当外部 object 指向 nil 的时候,object 理应被释放,但实际上 blockObject 依然强引用着 object,object 其实并没有被真正释放。因此ARC下的__block并不能阻止block对访问对象引用计数的增加。
3.2.2、添加__weak修饰符
- (void)testMallocBlock {
NSObject *object = [[NSObject alloc] init];
MyLog(@"Object", object);
__weak NSObject * weakObj = object;
MyLog(@"weakObject", weakObj);
void (^MallocBlock)(void) = ^() {
MyLog(@"Block weakObject", weakObj);
};
object = nil;
MyLog(@"Object", object);
MallocBlock();
MyLog(@"%@", MallocBlock);
}
2019-12-22 18:12:24.583423+0800 BlockBlog[10206:537851] 注释:Object_指针地址:0x7ffeec0b90a8_对象地址:0x600000ae03b0_对象:
2019-12-22 18:12:27.815357+0800 BlockBlog[10206:537851] 注释:weakObject_指针地址:0x7ffeec0b90a0_对象地址:0x600000ae03b0_对象:
2019-12-22 18:12:35.383486+0800 BlockBlog[10206:537851] 注释:Object_指针地址:0x7ffeec0b90a8_对象地址:0x0_对象:(null)
2019-12-22 18:12:38.105942+0800 BlockBlog[10206:537851] 注释:Block weakObject_指针地址:0x6000006a63f0_对象地址:0x0_对象:(null)
2019-12-22 18:12:41.872538+0800 BlockBlog[10206:537851] 注释:%@_指针地址:0x7ffeec0b9088_对象地址:0x6000006a63d0_对象:<__NSMallocBlock__: 0x6000006a63d0>
当object赋值 nil 时,block 内部的 weakObject 也为 nil 了,也就是说 object 实际上是被释放了。