Block的定义
Blocks是C语言的扩充功能。可以用一句话来表示Blocks的扩充功能:带有自动变量(局部变量)的匿名函数。
-
Block的语法声明:返回值类型 (^变量名)(参数列表) = ^ 返回值类型 (参数列表) 表达式
代码表示:
void (^block)(void) = ^void (void){};
或者使用
typedef
来定义一个block:typedef void (^block)(void);
分类
在iOS中,主要有三种Block类型,NSGlobalBlock
、NSStackBlock
、NSMallocBlock
。这三种block类型主要是以block所在的储存空间作为区分依据。
-
全局Block:
NSGlobalBlock
位于内存全局区,
.data
区域在Block内部,没有访问外部的变量
(__NSGlobalBlock__ : __NSGlobalBlock : NSBlock : NSObject)
-
栈Block:
NSStackBlock
位于内存的栈存储区,
在Block内部,访问外部变量,但是只读操作,不能进行"写"操作
(__NSStackBlock__ : __NSStackBlock : NSBlock : NSObject)
-
堆Block:
NSMallocBlock
位于内存的堆存储区
堆Block是栈Block的复制,因此在Block内部会访问外部变量,也是只读操作,不能进行"写"操作
(__NSMallocBlock__ : __NSMallocBlock : NSBlock : NSObject)
三种类型存储示意图:
-
Block的分类简单用下图表示
为了方便后续的分析和展示,需要把代码编译成.cpp
文件。编译指令如下
模拟器:
xcrun -sdk iphonesimulator clang -rewrite-objc xxx文件名
真机:
xcrun -sdk iphoneos clang -rewrite-objc xxx文件名
全局区Block(NSGlobalBlock)
-
没有外部变量引用的block就是全局区Block,如下代码:
//全局block NSGlobalBlock -(void)globalBlock{ void(^globalBlock)(void) = ^{ NSLog(@">>>>>globalBlock<<<<<"); }; NSLog(@"当前的block类型为>>>>>%@",globalBlock); globalBlock(); } ------------------------------------------------------ //打印结果为: 2022-04-20 14:38:22.668397+0800 suanfaProject[5151:219121] 当前的block类型为>>>>><__NSGlobalBlock__: 0x102b04cc0> 2022-04-20 14:38:22.668573+0800 suanfaProject[5151:219121] >>>>>globalBlock<<<<<
-
编译后的
.cpp
文件,很复杂,直截取需要的内容:struct __block_impl { //isa指针,指向具体的Block的类,当前是NSGlobalBlock类型的block,指向为__NSGlobalBlock__ void *isa; //表示一些block的附加信息,涉及到引用指数和销毁的判断,会使用到该值 int Flags; //保留变量 int Reserved; // 函数指针,指向具体的 block 实现的函数调用地址 //block将需要执行的代码创建一个函数,impl内部的FuncPtr指向这个函数的地址,通过地址调用这个函数,就可以执行block里面的代码 //函数名称类似于 __类名__调用block的方法名_block_func_标记数 :__BlockClang__globalBlock_block_func_0 void *FuncPtr; }; // @implementation BlockClang ///这个结构体就是block的实际内容。 struct __BlockClang__globalBlock_block_impl_0 { struct __block_impl impl; //block的描述信息, struct __BlockClang__globalBlock_block_desc_0* Desc; //结构体的同名构造函数 //fp:是block是根据执行的代码而创建的函数的函数指针 //desc:block的描述信息结构体 __BlockClang__globalBlock_block_impl_0(void *fp, struct __BlockClang__globalBlock_block_desc_0 *desc, int flags=0) { //在编译阶段所有类型的block的isa都指向_NSConcreteStackBlock,也就是说block类型是在运行时确定的。 impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; //block将需要执行的代码创建一个函数 static void __BlockClang__globalBlock_block_func_0(struct __BlockClang__globalBlock_block_impl_0 *__cself) { //里面是一句打印代码 NSLog((NSString*)&__NSConstantStringImpl__var_folders_sc_3y8t0nfj5zg81hbsf6y_dj9w0000gn_T_BlockClang_8949bb_mi_0); } //block的描述信息结构体,并给出一个默认值 static struct __BlockClang__globalBlock_block_desc_0 { size_t reserved; size_t Block_size; } __BlockClang__globalBlock_block_desc_0_DATA = { 0, sizeof(struct __BlockClang__globalBlock_block_impl_0)}; //-(void)globalBlock的方法,编译出来的文件 static void _I_BlockClang_globalBlock(BlockClang * self, SEL _cmd) { //void(*globalBlock)(void) = &__BlockClang__globalBlock_block_impl_0(__BlockClang__globalBlock_block_func_0, &__BlockClang__globalBlock_block_desc_0_DATA)); void(*globalBlock)(void) = ((void (*)())&__BlockClang__globalBlock_block_impl_0((void *)__BlockClang__globalBlock_block_func_0, &__BlockClang__globalBlock_block_desc_0_DATA)); //打印函数 NSLog((NSString *)&__NSConstantStringImpl__var_folders_sc_3y8t0nfj5zg81hbsf6y_dj9w0000gn_T_BlockClang_8949bb_mi_1,globalBlock); //调用globalBlock //去掉类型强转后的代码:globalBlock->FuncPtr(globalBlock); //即调用globalBlock结构体中FuncPtr参数,传值为block本身 ((void (*)(__block_impl *))((__block_impl *)globalBlock)->FuncPtr)((__block_impl *)globalBlock); } // @end
-
总结以上述代码
Block的输出为
__NSGlobalBlock__
,这也就说明了,是一种全局BlockBlock内部没有访问外部的auto变量
栈Block(NSStackBlock)
-
非强引用Block或者非
copy
的Block,类型就是栈Block。ARC
下需要使用__weak
修饰就是stackBlock
,代码实例如下//栈block NSStackBlock -(void)stackBlock{ int age = 10; void(^ stackBlock)(void) = ^{ NSLog(@">>>>>stackBlock<<<<<, age : %d",age); }; NSLog(@"当前的block类型为>>>>>%@",stackBlock); stackBlock(); } ------------------------------------------------------ //打印结果为: 2022-04-20 15:49:59.063036+0800 suanfaProject[5886:260026] 当前的block类型为>>>>><__NSStackBlock__: 0x16b5c5938> 2022-04-20 15:49:59.063267+0800 suanfaProject[5886:260026] >>>>>NSStackBlock<<<<<, age : 10
-
编译后的
.cpp
文件,如下struct __block_impl { //isa指针,指向具体的Block的类,当前是NSGlobalBlock类型的block,指向为__NSGlobalBlock__ void *isa; //表示一些block的附加信息,涉及到引用指数和销毁的判断,会使用到该值 int Flags; //保留变量 int Reserved; // 函数指针,指向具体的 block 实现的函数调用地址 //block将需要执行的代码创建一个函数,impl内部的FuncPtr指向这个函数的地址,通过地址调用这个函数,就可以执行block里面的代码 //函数名称类似于 __类名__调用block的方法名_block_func_标记数 :__BlockClang__globalBlock_block_func_0 void *FuncPtr; }; // @implementation BlockClang //block的结构体 struct __BlockClang__stackBlock_block_impl_0 { struct __block_impl impl; struct __BlockClang__stackBlock_block_desc_0* Desc; //这里多了一个age的参数,是和全局Block的文件不同的地方 int age; //结构体的同名构造函数 //fp:是block是根据执行的代码而创建的函数的函数指针 //desc:block的描述信息结构体 //多个一个age的赋值,看的出来这里是一个值的复制,并不是指针 __BlockClang__stackBlock_block_impl_0(void *fp, struct __BlockClang__stackBlock_block_desc_0 *desc, int _age, int flags=0) : age(_age) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; //block将需要执行的代码创建一个函数 static void __BlockClang__stackBlock_block_func_0(struct __BlockClang__stackBlock_block_impl_0 *__cself) { int age = __cself->age; // bound by copy //打印代码 NSLog((NSString *)&__NSConstantStringImpl__var_folders_sc_3y8t0nfj5zg81hbsf6y_dj9w0000gn_T_BlockClang_7d0b37_mi_0,age); } //block的描述信息结构体,并给出一个默认值 static struct __BlockClang__stackBlock_block_desc_0 { size_t reserved; size_t Block_size; } __BlockClang__stackBlock_block_desc_0_DATA = { 0, sizeof(struct __BlockClang__stackBlock_block_impl_0)}; //-(void)stackBlock的方法,编译出来的文件 static void _I_BlockClang_stackBlock(BlockClang * self, SEL _cmd) { //外部变量 int age = 10; // void(* stackBlock)(void) = &__BlockClang__stackBlock_block_impl_0(__BlockClang__stackBlock_block_func_0, &__BlockClang__stackBlock_block_desc_0_DATA, age)); void(* stackBlock)(void) = ((void (*)())&__BlockClang__stackBlock_block_impl_0((void *)__BlockClang__stackBlock_block_func_0, &__BlockClang__stackBlock_block_desc_0_DATA, age)); //打印函数 NSLog((NSString *)&__NSConstantStringImpl__var_folders_sc_3y8t0nfj5zg81hbsf6y_dj9w0000gn_T_BlockClang_7d0b37_mi_1,stackBlock); //调用globalBlock //去掉类型强转后的代码:stackBlock->FuncPtr(stackBlock); //即调用globalBlock结构体中FuncPtr参数,传值为block本身 ((void (*)(__block_impl *))((__block_impl *)stackBlock)->FuncPtr)((__block_impl *)stackBlock); } // @end
-
给Block内引用的外部变量
age
,进行复制操作,观察现象
由图上可知,在block内部使用block外部定义的局部变量时,在block内部是只读的,不能对他进行修改,如果想要修改,变量前要有__block修饰,或者使用static
修饰。 -
总结以上述代码
Block的输出为
__NSStackBlock__
,这也就说明了,是一种栈Block和全局Block不一样的地方,在于Block结构体中,有了需要引用的变量,而且在Block结构体初始化的时候,要进行变量的赋值
栈Block对于外部的变量,是以值复制的方式进行访问的。而且在编译阶段就已经完成了赋值,如果外部变量更改值,Block内部的变量值是不变的
在block内进行写操作,编译会报错,提示需要使用
__block
进行修饰。__block
的修饰原理,在下面会有说明。
堆Block(NSMallocBlock)
- 强引用Block或者
copy
后的block,都是堆Block,代码如下//堆block NSMallocBlock -(void)mallocBlock1{ int age = 10; //默认是强引用 void(^mallocBlock)(void) = ^{ NSLog(@">>>>>NSMallocBlock<<<<<, age : %d",age); }; NSLog(@"当前的block类型为>>>>>%@",mallocBlock); mallocBlock(); } ------------------------------------------------------ //打印结果为: 2022-04-21 14:39:23.197179+0800 suanfaProject[3997:179623] 当前的block类型为>>>>><__NSMallocBlock__: 0x6000011b4120> 2022-04-21 14:39:23.197343+0800 suanfaProject[3997:179623] >>>>>NSMallocBlock<<<<<, age : 10
- NSMallocBlock 类型的 block 通常不会在源码中直接出现,因为默认它是当一个 block 被 copy 的时候,才会将这个 block 复制到堆中。以下是一个 block 被 copy 时的示例代码 (来自 这里),可以看到,在图中框起来的代码,目标的 block 类型被修改为_NSConcreteMallocBlock。
Block的本质
-
前面说过了Block的分类,并且编译出了各个分类的代码,在编译出来的代码中,最主要的代码就是下面的这些:
在说明堆Block类型的时候,截图的代码中,是有一个void *_Block_copy(const void *arg)
方法。三种类型的Block在使用的过程中,都会调用这个方法。其在方法的第一步就是struct Block_layout *aBlock;
,获取到对应的Block,而Block_layout
结构体就是Block的数据结构。Block_layout
结构体展示如下:#define BLOCK_DESCRIPTOR_1 1 struct Block_descriptor_1 { uintptr_t reserved; uintptr_t size; }; #define BLOCK_DESCRIPTOR_2 1 struct Block_descriptor_2 { // requires BLOCK_HAS_COPY_DISPOSE BlockCopyFunction copy; BlockDisposeFunction dispose; }; #define BLOCK_DESCRIPTOR_3 1 struct Block_descriptor_3 { // requires BLOCK_HAS_SIGNATURE const char *signature; const char *layout; // contents depend on BLOCK_HAS_EXTENDED_LAYOUT }; struct Block_layout { void * __ptrauth_objc_isa_pointer isa; volatile int32_t flags; // contains ref count int32_t reserved; BlockInvokeFunction invoke; struct Block_descriptor_1 *descriptor; // imported variables };
-
在各个类型的Block打印中,可以看到各自对应的结构体类型
全局Block -----> NSGlobalBlock
栈Block. -----> NSStackBlock
堆Block -----> NSMallocBlock
在Block开放的源码中,
data.m
文件中可以发现,上述的3种Block都是继承自NSObject
。
-
综上
block
底层就是一个Block_layout
类型的结构体
,这个结构体中包含一个isa
指针,本质上是一个OC
对象block
是封装了函数调用
以及函数调用环境
的OC
对象
Block变量截获
-
为什么要进行变量的捕获?
经过前面Block类型的说明,了解到Block的三种类型,分别存放在内存全局区、内存栈区、内存堆区,不同的类型,销毁的时机不同。
在不同的Block内部使用外部变量的时候,需要考虑外部变量的生命周期,特别是销毁的情况
不能出现在Block内部使用的使用,外部变量已经销毁。
所以需要把外部变量捕获到Block的内部,这样的话,使用的时候就不需要管外部的局部变量是不是还存在,只要使用内部的捕获变量就行
-
C语言中变量分为三类
全局变量: 作用域在全局,哪个地方都能调用
-
局部变量:作用域在大括号中,只能在大括号内调用
局部自动变量
auto
关键字修饰局部静态变量
static
关键字修饰
-
变量是对象的情况下,也是一样的。对象类型的局部变量连所有权修饰符(__strong,__weak)一起捕获
栈Block:不管外部变量是
强引用还是弱引用
,block
都会弱引用
访问对象-
堆Block:
如果外部
强引用
,block
内部也是强引用
如果外部
弱引用
,block
内部也是弱引用
-
下面展示不同变量在Block内部的引用情况
- auto变量在Block内部的捕获情况
- 静态变量在Block内部的捕获情况
-
全局变量在Block内部的捕获情况
-
由第四条得处一下结论
__block修饰符
-
前面在Block分类里说过,如果要在Block内部修改引用的外部变量的值,直接修改是不允许的。编译系统给的提示是需要使用
__Block
修饰符。接下来就看一下__Block
的逻辑。
-
在报错的代码上进行修改,外部变量增加
__block
修饰,并且在Block内部修改变量的值。//堆block NSMallocBlock -(void)mallocBlock1{ //在Block需要引用的外部变量,添加__Block修饰符 __block int age = 10; //默认是强引用 void(^mallocBlock)(void) = ^{ //在Block内部修改变量的值 age = 30; NSLog(@">>>>>NSMallocBlock<<<<<, age : %d",age); }; NSLog(@"当前的block类型为>>>>>%@",mallocBlock); mallocBlock(); } ------------------------------------------------------ 打印结果为: 2022-04-22 14:26:14.442743+0800 suanfaProject[2562:85326] 当前的block类型为>>>>><__NSMallocBlock__: 0x60000092ebe0> 2022-04-22 14:26:14.442810+0800 suanfaProject[2562:85326] >>>>>NSMallocBlock<<<<<, age : 30
可以看出,加了
__block
后,编译不会报错,并且,修改变量的值成功。 -
接下来就编译出这段代码的
.cpp
文件进行查看// @implementation BlockClang //编译器将__block修饰的外部变量包装成一个结构体(对象),在结构体中创建一个同名变量, struct __Block_byref_age_0 { void *__isa; //结构体的指针指向 __Block_byref_age_0 *__forwarding; int __flags; int __size; //同名变量 int age; }; //Block的结构体,这个结构体就是block的实际内容。 struct __BlockClang__mallocBlock1_block_impl_0 { struct __block_impl impl; struct __BlockClang__mallocBlock1_block_desc_0* Desc; //block内部捕获根据外部变量创建的结构体指针 //将结构体copy到堆上,在block中使用自动变量时,使用指针指向的结构体中的自动变量,于是就达到了修改外部变量的作用。 __Block_byref_age_0 *age; // by ref //block结构体的同名构造 __BlockClang__mallocBlock1_block_impl_0(void *fp,struct __BlockClang__mallocBlock1_block_desc_0 *desc,__Block_byref_age_0 *_age,int flags=0) : age(_age->__forwarding) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; //-(void)mallocBlock1的方法,编译出来的文件 static void _I_BlockClang_mallocBlock1(BlockClang * self, SEL _cmd) { //把__block修饰的变量包装成__Block_byref_age_0结构体,并且玩层指针和变量的赋值 __attribute__((__blocks__(byref))) __Block_byref_age_0 age = {(void*)0,(__Block_byref_age_0 *)&age, 0, sizeof(__Block_byref_age_0), 10}; //获取实例化的block void(*mallocBlock)(void) = ((void (*)())&__BlockClang__mallocBlock1_block_impl_0((void *)__BlockClang__mallocBlock1_block_func_0, &__BlockClang__mallocBlock1_block_desc_0_DATA,(__Block_byref_age_0 *)&age,570425344)); //打印语句 NSLog((NSString *)&__NSConstantStringImpl__var_folders_sc_3y8t0nfj5zg81hbsf6y_dj9w0000gn_T_BlockClang_e5501c_mi_1,mallocBlock); //block的调用 ((void (*)(__block_impl *))((__block_impl *)mallocBlock)->FuncPtr)((__block_impl *)mallocBlock); } //block内部任务创建出来的方法 static void __BlockClang__mallocBlock1_block_func_0(struct __BlockClang__mallocBlock1_block_impl_0 *__cself) { __Block_byref_age_0 *age = __cself->age; // bound by ref //这里是给__block修饰的外部变量的赋值操作,可以看出使用的是根据变量的指针找到内存地址,然后进行的赋值 (age->__forwarding->age) = 30; //打印操作 NSLog((NSString *)&__NSConstantStringImpl__var_folders_sc_3y8t0nfj5zg81hbsf6y_dj9w0000gn_T_BlockClang_e5501c_mi_0,(age->__forwarding->age)); } /* //以下代码暂不关注 static struct __BlockClang__mallocBlock1_block_desc_0 { size_t reserved; size_t Block_size; void (*copy)(struct __BlockClang__mallocBlock1_block_impl_0*, struct __BlockClang__mallocBlock1_block_impl_0*); void (*dispose)(struct __BlockClang__mallocBlock1_block_impl_0*); } __BlockClang__mallocBlock1_block_desc_0_DATA = { 0, sizeof(struct __BlockClang__mallocBlock1_block_impl_0), __BlockClang__mallocBlock1_block_copy_0, __BlockClang__mallocBlock1_block_dispose_0 }; static void __BlockClang__mallocBlock1_block_copy_0(struct __BlockClang__mallocBlock1_block_impl_0*dst, struct __BlockClang__mallocBlock1_block_impl_0*src) {_Block_object_assign((void*)&dst->age, (void*)src->age, 8);} static void __BlockClang__mallocBlock1_block_dispose_0(struct __BlockClang__mallocBlock1_block_impl_0*src) {_Block_object_dispose((void*)src->age, 8);} */ // @end
-
对比一下没有使用
__block
的编译文件编译器会将__block修饰的变量包装成一个结构体(对象)
在结构体中新建一个同名变量
block内部捕获该结构体指针
在block中使用外部变量时,使用指针指向的结构体中的外部变量
-
在对结构体中的指针指向进行说明
如果Block是一个栈Block(NSStackBlock),结构体中的
__forwarding
指针指向的就是自己所在区域的地址-
如果Block是一个堆Block(NSMallocBlock),由于堆Block是栈Block复制生成的,即从栈区复制到了堆区。则在堆区会有一块属于Block的空间,也就会有变量结构体的空间。这个情况下,
__forwarding
指针指向的在堆区的内存地址。- a、当block在栈时,__Block_byref_a_0结构体内的__forwarding指针指向结构体自己
- b、当block被复制到堆中时,栈中的__Block_byref_age_0结构体也会被复制到堆中一份,而此时栈中的__Block_byref_a_0结构体中的__forwarding指针指向的就是堆中的__Block_byref_a_0结构体,堆中__Block_byref_a_0结构体内的__forwarding指针依然指向自己
-
c、通过__forwarding指针巧妙的将修改的变量赋值在堆中的__Block_byref_a_0中
block循环引用以及解决方法
循环引用的原因
-
引用计数算法(Reference Counting),对每个对象保存一个整型的引用计数器属性。用于记录对象被引用的情况。
对于一个对象 A,只要有任何一个对象引用了 A,则 A 的引用计数器就加1;
当引用被销毁时,引用放发通知给对象A,引用计数器就减1。
对象 A 的引用计数器一旦变为0,即表示对象 A 不可能再被使用,对象A被销毁。
-
正常释放流程
- 当A持有B,当A销毁后,会给B发送信号。B收到信号后,如果此时B的引用计数为0时,则B就会销毁,此时A,B都能正常释放。不会引起内存泄漏
-
循环引用
- 当A持有B,B同时也持有A时,此时A销毁需要B先销毁,而B销毁同样需要A先销毁,就导致相互等待销毁,此时A,B的引用计数都不为0,所以A,B此时都无法释放。 从而导致了内存泄漏
解决方法
-
平常代码里用的最多的应该就是
__weak
修饰符。定义一个弱引用的
weakSelf
,即__weak typeof(self) weakSelf = self;
,指向self
。因为是
__weak
修饰,变量的引用计数不会增加-
使用
__weak
修饰的代码展示- (void)weakSelf{ self.name = @"jack"; __weak typeof(self) weakSelf = self; void(^block)(void) = ^{ NSLog(@"weakSelf.name>>>>>%@",weakSelf.name); }; block(); } ------------------------------------------------------ 打印结果为: 2022-04-24 10:24:20.008072+0800 suanfaProject[1716:30969] weakSelf.name>>>>>jack
-
weak-strong-dance (弱强共舞)
-
如果只使用
__weak
修饰的变量,其引用计数不会增加。这样的话,会出现block内部持有的对象被提前释放的情况。比如在Block内部执行一个计时器任务,就会出现定时任务在执行的时候,退出控制器,控制器就被销毁,其变量获取不到- (void)weakSelfShortcoming{ self.name = @"jack"; __weak typeof(self) weakSelf = self; void(^block)(void) = ^{ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog(@"weakSelf.name>>>>>%@",weakSelf.name); }); }; block(); } ------------------------------------------------------ 2022-04-24 10:28:11.588892+0800 suanfaProject[1768:33723] ~~~ self dealloc ~~~ //可以看出,self已经被销毁,self的地址清空,也就获取不到weakSelf.name 2022-04-24 10:28:11.936690+0800 suanfaProject[1768:34131] weakSelf.name>>>>>(null)
-
Block内部嵌套定时任务,则需要同时使用
__weak
和__strong
。如果只用weak修饰
,则可能出现block内部持有的对象被提前释放,为了防止block内部变量被提前释放,使用__strong
对引用计数+1,防止提前释放。- (void)weak_strong_Self{ self.name = @"jack"; __weak typeof(self) weakSelf = self; void(^block)(void) = ^{ //在Block内部,使用__strong修饰 __strong typeof(weakSelf)strongSelf = weakSelf; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog(@"weakSelf.name>>>>>%@",strongSelf.name); }); }; block(); } ------------------------------------------------------ //可以看出,self是在定时任务完成以后进行销毁的 2022-04-24 10:35:18.498441+0800 suanfaProject[1869:38834] weakSelf.name>>>>>jack 2022-04-24 10:35:18.499136+0800 suanfaProject[1869:38688] ~~~ self dealloc ~~
-
-
__block
修饰变量- 代码如下
- (void)blockSymbol{ self.name = @"jack"; __block BlocksVC *blockVC = self; void(^block)(void) = ^{ NSLog(@"blockVC.name>>>>>%@",blockVC.name); }; block(); } ------------------------------------------------------ 2022-04-24 10:48:00.279485+0800 suanfaProject[2049:46359] blockVC.name>>>>>jack 2022-04-24 10:48:00.625749+0800 suanfaProject[2049:46303] ~~~ self dealloc ~~~
- 代码如下
-
把对象作为Block的参数,传到Block内部
-
代码如下
self.name = @"jack"; void(^block)(BlocksVC *blockVC) = ^(BlocksVC *blockVC){ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog(@"blockVC.name>>>>>%@",blockVC.name); }); }; block(self); } ------------------------------------------------------ 2022-04-24 10:56:38.622366+0800 suanfaProject[2154:51224] blockVC.name>>>>>jack 2022-04-24 10:56:38.623024+0800 suanfaProject[2154:51097] ~~~ self dealloc ~~~</pre>
-