O bloco é frequentemente usado no desenvolvimento, então saberemos o que é e por quê.
O que é bloco?
O bloco pode encapsular uma função anônima como um objeto, capturar os dados exigidos pelo contexto e passá-los ao objeto de destino para retorno de chamada quando apropriado. O objetivo do nosso uso Block
é, na verdade, passar valores por meio de retornos de chamada, então vamos dar uma olhada na Block
camada inferior e aprender mais sobre ela Block
.
Térreo do bloco
Block
A implementação subjacente de é uma estrutura semelhante à implementação subjacente de uma classe. Ela possui isa
ponteiros e Block pode ser considerado um objeto.
Block_layout
É block
a estrutura subjacente da estrutura e seu código-fonte é o seguinte:
// Block 结构体
struct Block_layout {
//指向表明block类型的类
void *isa;//8字节
//用来作标识符的,类似于isa中的位域,按bit位表示一些block的附加信息
volatile int32_t flags; // contains ref count 4字节
//保留信息,可以理解预留位置,用于存储block内部变量信息
int32_t reserved;//4字节
//函数指针,指向具体的block实现的调用地址
BlockInvokeFunction invoke;
//block的附加信息
struct Block_descriptor_1 *descriptor;
// imported variables
};
isa
: Ponteiro para a classe a qual pertence, ou seja, o tipo do Bloco;flags
: Variável flag, usada para marcar tipo de bloco, status, etc.// flags 标识 // Values for Block_layout->flags to describe block objects enum { //释放标记,一般常用于BLOCK_BYREF_NEEDS_FREE做位与运算,一同传入flags,告知该block可释放 BLOCK_DEALLOCATING = (0x0001), // runtime //存储引用引用计数的 值,是一个可选用参数 BLOCK_REFCOUNT_MASK = (0xfffe), // runtime //低16位是否有效的标志,程序根据它来决定是否增加或者减少引用计数位的值 BLOCK_NEEDS_FREE = (1 << 24), // runtime //是否拥有拷贝辅助函数,(a copy helper function)决定block_description_2 BLOCK_HAS_COPY_DISPOSE = (1 << 25), // compiler //是否拥有block C++析构函数 BLOCK_HAS_CTOR = (1 << 26), // compiler: helpers have C++ code //标志是否有垃圾回收,OSX BLOCK_IS_GC = (1 << 27), // runtime //标志是否是全局block BLOCK_IS_GLOBAL = (1 << 28), // compiler //与BLOCK_HAS_SIGNATURE相对,判断是否当前block拥有一个签名,用于runtime时动态调用 BLOCK_USE_STRET = (1 << 29), // compiler: undefined if !BLOCK_HAS_SIGNATURE //是否有签名 BLOCK_HAS_SIGNATURE = (1 << 30), // compiler //使用有拓展,决定block_description_3 BLOCK_HAS_EXTENDED_LAYOUT=(1 << 31) // compiler };
reserved
: variáveis reservadasinvoke
: Ponteiro de função chamado quando o bloco é executado. A chamada do bloco é realmente chamada.invoke
descriptor
: Descrição detalhada do bloco, incluindo funções de cópia/descarte, usadas ao lidar com blocos que fazem referência a variáveis externas. Existem três categoriasBlock_descriptor_1
é necessárioBlock_descriptor_2
eBlock_descriptor_3
são opcionais
#define BLOCK_DESCRIPTOR_1 1 struct Block_descriptor_1 { uintptr_t reserved;//保留信息 uintptr_t size;//block大小 }; //当block引用对象时,就会新增如下结构: #define BLOCK_DESCRIPTOR_2 1 struct Block_descriptor_2 { // requires BLOCK_HAS_COPY_DISPOSE BlockCopyFunction copy;//拷贝函数指针 BlockDisposeFunction dispose; }; // 当block有签名时,增加Block_descriptor_3: #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 布局 };
O acima descriptor
pode ser refletido em seu construtor, no qual Block_descriptor_2
e são obtidos Block_descriptor_3
através Block_descriptor_1
do endereço de内存平移
static struct Block_descriptor_1 * _Block_descriptor_1(struct Block_layout *aBlock)
{
return aBlock->descriptor;//默认打印
}
#endif
// Block 的描述 : copy 和 dispose 函数
static struct Block_descriptor_2 * _Block_descriptor_2(struct Block_layout *aBlock)
{
if (! (aBlock->flags & BLOCK_HAS_COPY_DISPOSE)) return NULL;
uint8_t *desc = (uint8_t *)aBlock->descriptor;//descriptor_1的地址
desc += sizeof(struct Block_descriptor_1);//通过内存平移获取
return (struct Block_descriptor_2 *)desc;
}
// Block 的描述 : 签名相关
static struct Block_descriptor_3 * _Block_descriptor_3(struct Block_layout *aBlock)
{
if (! (aBlock->flags & BLOCK_HAS_SIGNATURE)) return NULL;
uint8_t *desc = (uint8_t *)aBlock->descriptor;
desc += sizeof(struct Block_descriptor_1);
if (aBlock->flags & BLOCK_HAS_COPY_DISPOSE) {
desc += sizeof(struct Block_descriptor_2);
}
return (struct Block_descriptor_3 *)desc;
}
- variáveis: variáveis fora do escopo do bloco. Se o bloco não chama nenhuma variável externa, a variável não existe.
Tipo de bloco
block
Existem três tipos principais
- bloco global (
_NSConcreteGlobalBlock
) - pilha (
_NSConcreteStackBlock
) - pilha (
_NSConcreteMallocBlock
)
Área globalBloco
- (void)checkBlock {
//不引用任何变量
void(^block1)(void) = ^{
NSLog(@"wuwuFQ:checkBlock");
};
//有形参有返回值
int(^block2)(int, int) = ^(int a, int b){
//a + b
return a + b;
};
//引用静态变量
static int a = 0;
void(^block3)(void) = ^{
NSLog(@"wuwuFQ:%d", a);
};
NSLog(@"wuwuFQ:block1%@", block1);
NSLog(@"wuwuFQ:block2%@", block2);
NSLog(@"wuwuFQ:block3%@", block3);
}
Block
Se nenhuma variável externa for referenciada ou apenas variáveis globais ou variáveis estáticas forem referenciadas, elas serão globais.Block
Bloco de área de pilha
- (void)checkBlock {
int a = 10;
void(^block)(void) = ^{
NSLog(@"wuwuFQ:%d", a);
};
NSLog(@"wuwuFQ:%@", block);
}
Ao acessar variáveis externas neste momento , a operação de referência forte block
ocorre por padrão , portanto é a área de heap.copy
Block
Bloco de área de pilha
- (void)checkBlock {
//引用局部变量a
int a = 0;
// Assigning block literal to a weak variable; object will be released after assignment
void (^__weak block)(void) = ^{
NSLog(@"wuwuFQ:%d", a);
};
NSLog(@"wuwuFQ:%@", block);
}
Por __weak
não realizar uma holding forte, block
ainda é uma área de stack.block
Resumir
- Quando variáveis externas não são referenciadas, o bloco está na área global, seja ele usado como atributo ou parâmetro.
- Ao fazer referência a variáveis externas, o bloco é declarado na área da pilha, mas quando o bloco é chamado ou atribuído a outras variáveis fortemente modificadas, ocorrerá uma operação de cópia, copiando da área da pilha para a área do heap.
Bloco captura variáveis externas
Captura de variáveis ordinárias
O bloco captura variáveis externas ordinárias (não __block
variáveis aguardando modificação) e gera automaticamente um atributo para salvar.
#import <UIKit/UIKit.h>
int main(int argc, char * argv[]) {
int a = 11;
void (^block)(void) = ^(void){
NSLog(@"%@",@(a));
};
block();
}
Block gera automaticamente uma __main_block_impl_0
estrutura ao compilar
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;// 里面有内存管理函数,Block_size表示block的大小
int *static_k; // 捕获到的局部静态变量
int a; //自动生成a属性来保存捕获的变量a:捕获到的普通局部变量
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _a, int flags=0) : a(_a) {
// 构造函数
impl.isa = &_NSConcreteStackBlock;//isa指针
impl.Flags = flags;//
impl.FuncPtr = fp;//fp函数作为参数传入,保存在FuncPtr指针,这样block()才能调用函数
Desc = desc;
}
};
__main_block_impl_0
A variável temporária personalizada da estrutura int a é usada para capturar a variável externa a. O a interno na verdade não é a variável a externa do bloco. Isso também explica por que a não pode ser modificado no bloco.
Captura da variável __block
__block int a = 11;
void (^block)(void) = ^(void){
a++;
NSLog(@"%@",@(a));
};
block();
O código da estrutura do bloco __main_block_impl_0
neste momento é o seguinte:
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_a_0 *a; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_a_0 *_a, int flags=0) : a(_a->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
A estrutura neste momento __main_block_impl_0
é basicamente a mesma da captura de objetos comuns, a única diferença está na captura de variáveis externas. Existe mais uma estrutura __Block_byref_a_0
. Essa estrutura é na verdade usada pelo Block para capturar __block int a
variáveis. O código-fonte da estrutura é o seguinte:
struct __Block_byref_a_0 {
void *__isa;
__Block_byref_a_0 *__forwarding;// 保存变量a的指针
int __flags;
int __size;
int a;// 保存变量a的值
};
Pode-se observar que o Block não apenas captura __block int a
o valor da variável, mas também captura a
o ponteiro, que é armazenado nos atributos e através __Block_byref_a_0
da estrutura respectivamente . Desta forma, o interno e o externo apontam para o mesmo endereço, então o externo variáveis podem ser modificadas.int a
__Block_byref_a_0 *__forwarding
Block
Bloco de compreensão
Eu li muitos artigos e arranhei Block
a superfície, então o que você Block
entendeu?
Block
Dividido em três tipos: bloco global (_NSConcreteGlobalBlock
), bloco de pilha (_NSConcreteStackBlock
), bloco heap (_NSConcreteMallocBlock
),Block
Não acesse variáveis externas, nem acesse variáveis globais ou variáveis estáticas. A memória do bloco está na área global.Block
O acesso a variáveis externasBlock
é declarado na área de pilha.Se ocorrer durante o usocopy
( não ocorrerá__weak
modificação ), a memória estará na área de heap.block
copy
Block
Block
Faça referência a variáveis locais (int a
;NSString *str
),Block
a memória está na área de heap, o valor é copiado e o valor da variável não pode ser modificado.Block
Faça referência à variável local modificada por __block.A memória do bloco fica na área heap, acionando_Block_byref_copy
a função para copiar o ponteiro, e o valor da variável pode ser modificado.Block
Referências a objetos comuns (self.view
;self.name
),Block
a memória está na área de heap, aciona a cópiaARC
do ponteirocopy
eretain
faz a contagem de referências+1
(uma extensão aqui: Na verdade, Block também contém variáveis locais, mas o escopo das variáveis locais é pequeno e seráBlock
liberado com o passar do tempo) , desde que o objeto não seja seguradoBlock
, o objeto será realmenteBlock
liberado de acordo)
__main_block_impl_0
A variável ou objeto é uma estrutura dentro do Bloco .__main_block_func_0
Quando a função é chamada e executada, o tipo de referência (variável global, variável local, objeto, modificação, modificação, etc. )__main_block_func_0
será julgado e diferentes princípios serão implementados.__block
__weak
Block
copy