Desenvolvimento iOS: um resumo da pesquisa sobre o uso do Block

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 Blockcamada inferior e aprender mais sobre ela Block.

Térreo do bloco

BlockA implementação subjacente de é uma estrutura semelhante à implementação subjacente de uma classe. Ela possui isaponteiros e Block pode ser considerado um objeto.

Block_layoutÉ blocka 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 ​​reservadas
  • invoke: 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 categorias
    • Block_descriptor_1é necessário
    • Block_descriptor_2e Block_descriptor_3sã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 descriptorpode ser refletido em seu construtor, no qual Block_descriptor_2e são obtidos Block_descriptor_3através Block_descriptor_1do 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

blockExistem 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);
}

Adicione a descrição da imagem
BlockSe 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);
}

Adicione a descrição da imagem
Ao acessar variáveis ​​externas neste momento , a operação de referência forte blockocorre por padrão , portanto é a área de heap.copyBlock

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);
}

Adicione a descrição da imagem
Por __weaknão realizar uma holding forte, blockainda é 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 __blockvariá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_0estrutura 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_0A 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_0neste 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 avariá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 ao valor da variável, mas também captura ao ponteiro, que é armazenado nos atributos e através __Block_byref_a_0da 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 *__forwardingBlock

Bloco de compreensão

Eu li muitos artigos e arranhei Blocka superfície, então o que você Blockentendeu?

  • BlockDividido em três tipos: bloco global (_ NSConcreteGlobalBlock), bloco de pilha ( _NSConcreteStackBlock), bloco heap ( _NSConcreteMallocBlock),
  • BlockNão acesse variáveis ​​externas, nem acesse variáveis ​​globais ou variáveis ​​estáticas. A memória do bloco está na área global.
  • BlockO acesso a variáveis ​​​​externas Blocké declarado na área de pilha.Se ocorrer durante o uso copy( não ocorrerá __weakmodificação ), a memória estará na área de heap.blockcopyBlock
  • BlockFaça referência a variáveis ​​locais ( int a; NSString *str), Blocka memória está na área de heap, o valor é copiado e o valor da variável não pode ser modificado.
  • BlockFaça referência à variável local modificada por __block.A memória do bloco fica na área heap, acionando _Block_byref_copya função para copiar o ponteiro, e o valor da variável pode ser modificado.
  • BlockReferências a objetos comuns ( self.view; self.name), Blocka memória está na área de heap, aciona a cópia ARCdo ponteiro copye retainfaz 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á Blockliberado com o passar do tempo) , desde que o objeto não seja segurado Block, o objeto será realmente Blockliberado de acordo)

__main_block_impl_0A variável ou objeto é uma estrutura dentro do Bloco . __main_block_func_0Quando 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_0será julgado e diferentes princípios serão implementados.__block__weakBlockcopy

Acho que você gosta

Origin blog.csdn.net/wujakf/article/details/129041644
Recomendado
Clasificación