block系列1-block使用

1.使用Blocks


(1)block概念

Blocks是带有自动变了(局部变量)的匿名函数。其写法遵循BN范式

^ 返回值类型 参数列表 表达式

返回值类型可省略,参数列表亦可省略。可以定一个Blocks变量来获得匿名函数的使用权

int (^blk)(int) = ^(int count){return count+1};

我们可以作为参数传递Blocks变量,也可以返回Blocks类型变量,也可以使用typedef定义更方便使用Blocks变量

void func(int (^blk)(int)){}

int (^func())(int){
    return ^(int count){return count+1};
}

typedef int (^blc_t)(int);
  • block的代码是内联的,效率高于函数调用
  • block对于外部变量默认是只读属性,要在block内修改外部变量需要添加_block关键字修饰
  • block被Objective-C看成是对象处理

block会捕获在其定义时的变量值,变量修改后并不会影响block中使用的变量。

{
    int a = 10;
    int (^blc)(int) = int ^(int count){return count+a;};
    a++;
    NSLog(@"%@",blc(5));//打印15,而不是16
}

变量的复制关系如下

block外变量引用,默认是复制到block内的readonly变量

[站外图片上传中...(image-7c89d1-1524555790555)]

对于用__ block修饰的外部变量引用,block复制其引用地址来实现访问

[站外图片上传中...(image-a85bf0-1524555790555)]

(2)block常见用法:

  1. 局部位置声明一个Block型的变量

    return_type (^blockName)(var_type) = ^return_type (var_type varName) {
        // ...
    };
    blockName(var);
    
  2. @interface声明Block型属性

    @property(nonatomic, copy)return_type (^blockName) (var_type);
    
  3. Block型作为形参

    - (void)yourMethod:(return_type (^)(var_type))blockName;
    
  4. 内联用法,定义后立即调用,不常用

    ^return_type (var_type varName)
    {
        //...
    }(var);
    
  5. 递归调用

    使用__block避免循环引用问题。

    __block return_type (^blockName)(var_type) = [^return_type (var_type varName)
    {
        if (returnCondition)
        {
            blockName = nil;
            return;
        }
        // ...
        // 【递归调用】
        blockName(varName);
    } copy];
    
    【初次调用】
    blockName(varValue);
    
  6. 作为返回值

    - (return_type(^)(var_type))methodName
    {
        return ^return_type(var_type param) {
            // ...
        };
    }
    

2.block原理


(1)数据结构定义

block的数据结构定义如下

[站外图片上传中...(image-894ad2-1524555790555)]

结构体定义如下

#define BLOCK_DESCRIPTOR_1 1
struct Block_descriptor_1 {
    unsigned long int reserved;
    unsigned long int size;
};
struct Block_layout {
    void *isa;
    volatile int flags; // contains ref count
    int reserved; 
    void (*invoke)(void *, ...);
    struct Block_descriptor_1 *descriptor;
    // imported variables
};
//Block-private.h

对于每个参数解释如下

  1. isa: 对象指针
  2. flags: 记录block的一些附加信息,包括引用计数
  3. reserved: 保留变量。
  4. invoke: 函数指针,指向block函数实现地址。
  5. descriptor:附加信息描述
//falgs bit位描述如下
enum {
    BLOCK_DEALLOCATING =      (0x0001),  // runtime
    BLOCK_REFCOUNT_MASK =     (0xfffe),  // runtime
    BLOCK_NEEDS_FREE =        (1 << 24), // runtime
    BLOCK_HAS_COPY_DISPOSE =  (1 << 25), // compiler
    BLOCK_HAS_CTOR =          (1 << 26), // compiler: helpers have C++ code
    BLOCK_IS_GC =             (1 << 27), // runtime
    BLOCK_IS_GLOBAL =         (1 << 28), // compiler
    BLOCK_USE_STRET =         (1 << 29), // compiler: undefined if !BLOCK_HAS_SIGNATURE
    BLOCK_HAS_SIGNATURE  =    (1 << 30)  // compiler
};

从枚举变量的定义可以看出flags的bit位作用,第0位表示释放内存标志,1-23bit作为引用计数值,24-31略过。

在OC中有三种block

  1. _NSConcreteGlobalBlock 全局的静态 block,不会访问任何外部变量。
  2. _NSConcreteStackBlock 保存在栈中的 block,当函数返回时会被销毁,ARC下不适用。
  3. _NSConcreteMallocBlock 保存在堆中的 block,当引用计数为 0 时会被销毁。

下篇文章讲解不同block的实现方式

你可能感兴趣的:(block系列1-block使用)