概述
在 iOS 开发中,Block 是一种非常重要的编程概念,它能够将代码块作为参数传递和回来,从而便利实现回调、异步任务和代码封装等功能。在本文中,我们将深入探讨 Block 的技术细节,包括 Block 的界说、分类、内存管理、循环引证及其处理方案等
界说
Block 是一种由苹果引入的 Objective-C 扩展,是一种代码块目标,能够像普通目标相同存储到变量中,也能够作为参数传递和回来。其本质是一个封装函数调用以及函数调用环境的OC目标,其内部也有个isa指针在 Objective-C 中,Block 有三种类型:大局 Block、栈 Block 和堆 Block
大局 Block:大局 Block 是指它的效果域在整个应用程序中都能够拜访,因为它存储在大局数据区,而且体系会在应用程序启动时主动创立。
栈 Block:栈 Block 是指它的效果域只在当时函数内部,当它超出效果域时,它会主动被毁掉。栈 Block 只能拜访当时函数的变量,不能拜访大局变量和静态变量。
堆 Block:堆 Block 是指它的效果域在整个应用程序中都能够拜访,因为它被存储在堆内存中。当堆 Block 被赋值给一个变量时,该变量会对其进行引证计数,而且在没有任何引证时,堆 Block 会主动被毁掉。
源码分析
struct __block_impl {
void *isa; // isa指针,指向一个类目标,有三种类型:
int Flags; // block 的负载信息(引证计数和类型信息),按位存储
int Reserved; // 保存变量
void *FuncPtr; // 一个指针,指向Block执行时调用的函数,也便是Block需求执行的代码块
};
struct block_descriptor {
size_t reserved; // Block版别升级所需的预留区空间,在这里为0。
size_t Block_size; // Block巨细(sizeof(struct __blockTest_block_impl_0))
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct block_descriptor* descriptor;
...
// 构造函数
__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;
}
};
变量捕获(capture)
为了保证block内部能够正常拜访外部变量,block有个变量捕获机制
block内部拜访目标类型的auto变量
假如block是在栈上,将不会对auto变量发生强引证block拷贝到堆上的进程
-
调用block内部的copy函数,copy函数内部会调用_Block_object_assign函数
-
_Block_object_assign函数会依据auto变量的润饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,构成强引证(retain)或者弱引证
// __block变量 变量名为a _Block_object_assign((void*)&dst->a, (void*)src->a, 8/BLOCK_FIELD_IS_BYREF/);
// __block目标类型的auto变量 变量名为p _Block_object_assign((void*)&dst->p, (void*)src->p, 3/BLOCK_FIELD_IS_OBJECT/);
block从堆上移除的进程
- 调用
block内部的dispose函数,dispose函数内部会调用_Block_object_dispose函数 -
_Block_object_dispose函数会主动开释引证的auto变量(release)
定论如下
- 假如
block在栈空间,不论外部变量是强引证还是弱引证,都不会对变量发生强引证 - 假如
block在堆空间,假如外部强引证,block内部也是强引证;假如外部弱引证,block内部也是弱引证
__block润饰符
界说:__block会将变量包装成一个OC目标
效果:用于处理block内部无法修正auto变量值的问题
__block int age = 10
struct __Block_byref_age_0 {
void *__isa;
__Block_byref_age_0 *__forwarding;//age的地址
int __flags;
int __size;
int age;//age 的值
};
注意:
- __block不能润饰大局变量、静态变量
- __Block_byref_age_0结构体内部地址和外部变量age是同一地址
block内部拜访__block润饰的目标类型
假如block是在栈上,将不会对指向的目标发生强引证当__block变量被copy到堆时
- 会调用
__block变量内部的copy函数 -
copy函数内部会调用_Block_object_assign函数 -
_Block_object_assign函数会依据所指向目标的润饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,构成强引证(retain)或者弱引证(注意:这里仅限于ARC时会retain,MRC时不会retain)
假如__block变量从堆上移除
- 会调用
__block变量内部的dispose函数 - dispose函数内部会调用
_Block_object_dispose函数,_Block_object_dispose函数会主动开释指向的目标(release)
处理循环引证问题 – ARC
1、用__weak、__unsafe_unretained处理
__weak typeof(self) weakSelf = self;
// __unsafe_unretained id weakSelf = self;
self.block = ^{
NSLog(@"%p", weakSelf);
}
2、用__block处理(必需要调用block,而且置目标为nil)
__block id weakSelf = self;
self.block = ^{
NSLog(@"%p", weakSelf);
weakSelf = nil;
}
self.block();
处理循环引证问题 – MRC
1、用__weak、__unsafe_unretained处理
// 因为MRC环境下,不支持__weak
__unsafe_unretained id weakSelf = self;
self.block = ^{
NSLog(@"%p", weakSelf);
}
2、用__block处理
__block id weakSelf = self;
self.block = ^{
NSLog(@"%p", weakSelf);
}




