Block 知识

定义

Block 是C语言的扩充功能,是一个带有自动变量(局部变量)的匿名函数

block在c++下面的实现(Block 本质)

  • oc 代码 常规
int main(int argc, const char * argv[]) {

    void (^blk)(void) = ^{
        
        NSLog(@"BlockPrint");
    };
    
    blk();
    return 0;
}
  • 转换成.cpp代码
//其结构为今后版本升级所需的区域以及函数指针
struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};


//__main_block_impl_0 结构体声明如下:
struct __main_block_impl_0 {

  //第一个成员变量impl,其定义在上面,有四个成员变量
  struct __block_impl impl;

  //第二个成员变量Desc,其定义在下面,有2个成员变量【所需的区域与block大小】
  struct __main_block_desc_0* Desc;

  //构造函数初始化
  __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;
  }
};

//参数__cself 是结构体__main_block_impl_0 的指针
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_jw_5qlq_4rj54j7dqfnvnfn_lq40000gn_T_main_c23e5d_mi_0);
    }

//所需的区域与block大小
static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};


int main(int argc, const char * argv[]) {

    //太复杂,简化一下:
    //struct  __main_block_impl_0 tmp = __main_block_impl_0(__main_block_func_0,& __main_block_desc_0_DATA);
    //struct __main_block_impl_0 *blk = *tmp  
    //该源代码将__main_block_impl_0 结构体类型的自动变量,即栈上生成的__main_block_impl_0 结构体实例的指针,赋值给__main_block_impl_0结构体指针类型的变量blk,
//struct __main_block_impl_0 *blk = *tmp 部分的代码对应的最初的源代码
//将Block 语法生成的Block赋给Block类型变量blk,它等于将__main_block_impl_0结构体实例的指针赋值给blk。
 
void (^blk)(void) = ^{
        NSLog(@"BlockPrint"); };
    void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
    
    //函数指针调用函数
    //(*blk ->impl.FuncPtr)(blk);
    ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
    return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };

解释:
struct __main_block_impl_0 tmp = __main_block_impl_0(__main_block_func_0,& __main_block_desc_0_DATA);

  • 第一个参数:Block语法转换的C语言函数指针
  • 第二个参数:作为静态全局变量初始化的__main_block_impl_0结构体实例指针

由此可以看出,该源代码使用Block,即__main_block_impl_0 结构体实例的大小,进行初始化

该结构体初始化如下:

struct __main_block_impl_0 {

  //这一部分为__block_impl
  void *isa = &_NSConcreteStackBlcok;  //将Block指针赋值给Block结构体成员变量isa
  int Flags = 0;
  int Reserved = 0;
  void *FuncPt = __main_block_func_0;

  //这一部分为__main_block_desc_0
  Desc = & __main_block_desc_0_DATA;

};

Block 本质上还是Objective-C的对象

  • oc 代码 截取自动变量值
#import 

int main(int argc, const char * argv[]) {

    
    int dmy = 256;
    int val = 10;
    const char *fmt = "val = %d\n";
    void (^blk)(void) = ^{
        printf(fmt,val);
    };
    
    val = 2;
    fmt = "there values were change val = %d\n";
    
    blk();
    return 0;
}


  • 转换成.cpp代码
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;

  //自动变量被当作成员变量追加到__main_block_impl_0 结构体中【fmt、val】
 //没有使用的变量不回追加【dym】
  const char *fmt;
  int val;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, const char *_fmt, int _val, int flags=0) : fmt(_fmt), val(_val) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  
  //仅仅截获自动变量的值:重新定义了一个变量,仅仅是取而已,所以不会改变
  const char *fmt = __cself->fmt; // bound by copy
  int val = __cself->val; // bound by copy

        printf(fmt,val);
    }

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main(int argc, const char * argv[]) {


    int dmy = 256;
    int val = 10;
    const char *fmt = "val = %d\n";
    void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, fmt, val));

    val = 2;
    fmt = "there values were change val = %d\n";

    ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
    return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };

所谓的“截取自动变量”意味着 执行block语法时,Block 语法表达式所使用的自动变量值被保存到Block的结构体实例(Block自身)中

因为在实现上不能改写被截获自动变量的值,所以当编译器在编译过程中检出被截获自动变量赋值的操作时,便产生编译错误

为了解决上述这个问题,引入全局变量、静态变量、静态全局变量

#import 

int global_val = 1;
static int static_global_val = 2;
int main(int argc, const char * argv[]) {

    
    static int static_val = 3;
    void (^blk)(void) = ^{
        global_val *= 1;
        static_global_val *= 2;
        static_val *= 3;
        
    };
    
   
    blk();
    return 0;
}
  • 转换成.cpp代码


int global_val = 1;
static int static_global_val = 2;

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int *static_val;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_static_val, int flags=0) : static_val(_static_val) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  int *static_val = __cself->static_val; // bound by copy

        global_val *= 1;
        static_global_val *= 2;
        (*static_val) *= 3;

    }

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main(int argc, const char * argv[]) {


    static int static_val = 3;
    void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, &static_val));


    ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
    return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };

比较,不同的地方如下:

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  int *static_val = __cself->static_val; // bound by copy

        global_val *= 1;
        static_global_val *= 2;
        (*static_val) *= 3;

    }


struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  
  //****************
  int *static_val;
  //****************

  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_static_val, int flags=0) : static_val(_static_val) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }

  • 使用静态变量global_val的指针对其进行访问,将静态变量global_val的指针传递给__main_block_impl_0结构体的构造函数并保存

  • 而全局的变量并没有当作成员变量追加到__main_block_impl_0 结构体中【global_val、static_global_val】,而是直接赋值,(不是 仅仅截获自动变量的值:重新定义了一个变量,仅仅是取而已,所以不会改变)

  • 解决Block中不能保存值这一问题,引入__block


#import 


int main(int argc, const char * argv[]) {

    
    __block int val = 1;
    void (^blk)(void) = ^{
        val = 2;
        
    };
    
   
    blk();
    return 0;
}

struct __Block_byref_val_0 {
  void *__isa;

//__forwarding持有指向该实例本身的指针
__Block_byref_val_0 *__forwarding;
 int __flags;
 int __size;
 int val;
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;

  //********这里变化了,发现原来是一个结构体*******
  __Block_byref_val_0 *val; // by ref
  //********这里变化了********

  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_val_0 *_val, int flags=0) : val(_val->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  __Block_byref_val_0 *val = __cself->val; // bound by ref

        (val->__forwarding->val) = 2;

    }
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->val, (void*)src->val, 8/*BLOCK_FIELD_IS_BYREF*/);}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->val, 8/*BLOCK_FIELD_IS_BYREF*/);}

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
  void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
int main(int argc, const char * argv[]) {


    __attribute__((__blocks__(byref))) __Block_byref_val_0 val = {(void*)0,(__Block_byref_val_0 *)&val, 0, sizeof(__Block_byref_val_0), 1};
    void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_val_0 *)&val, 570425344));


    ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
    return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };

我们发现:只要在局部变量上附加了 __block 说明说,源代码量就急剧增加
在结构体__main_block_impl_0 中多了一个成员变量 __Block_byref_val_0 *val; // by ref,查找方法是一个结构体【结构体类型的自动变量】


//意味着该结构体持有相当于原始自动变量的成员变量
struct __Block_byref_val_0 {
  void *__isa;
__Block_byref_val_0 *__forwarding;
 int __flags;
 int __size;
 int val;
};


//源码: val = 2;
//在前面使用静态变量时候,使用了指向静态变量的指针,而现在更复杂
//使用了 结构体指针
//__main_block_impl_0 结构体实例持有指向__Block_byref_val_0 结构体实例的指针
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  __Block_byref_val_0 *val = __cself->val; // bound by ref

        (val->__forwarding->val) = 2;

    }
Block 知识_第1张图片
__block.png

Block 存储区域

通过上面可知:

Block 转换为 Block的结构体类型的自动变量【栈上生成的该结构体的实例
__Block 变量 转换为 __Block 变量的结构体类型的自动变量
所谓结构体类型的自动变量即 栈上生成的该结构体的实例

查看impl.isa = &_NSConcreteStackBlock;

//__Block 变量转换为__Block 变量的结构体类型的自动变量
struct __Block_byref_val_0 {
  void *__isa;
__Block_byref_val_0 *__forwarding;
 int __flags;
 int __size;
 int val;
};

//Block 转换为Block的结构体类型的自动变量【`栈上生成的该结构体的实例`】
struct __main_block_impl_0 {

  //第一个成员变量impl,其定义在上面,有四个成员变量
  struct __block_impl impl;

  //第二个成员变量Desc,其定义在下面,有2个成员变量【所需的区域与block大小】
  struct __main_block_desc_0* Desc;

  //构造函数初始化
  __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;
  }
};

如下图标:

名称 实质
Block 栈上Block的结构体实例
__ Block变量 栈上__Block变量的结构体实例
  • Block的类
设置对象的存储区域
_NSConcreteStackBlock
_NSConcreteGlobalBlock 程序的数据区域(.data 区)
_NSConcreteMallocBlock

具体分析:

  • o c 代码全局的block
#import 

//全局的block
void (^blk)(void) = ^{
    printf("Global Block\n");
};

int main(int argc, const char * argv[]) {

    return 0;
}
  • .cpp 代码如下

struct __blk_block_impl_0 {
  struct __block_impl impl;
  struct __blk_block_desc_0* Desc;
  __blk_block_impl_0(void *fp, struct __blk_block_desc_0 *desc, int flags=0) {

    //*******************
    impl.isa = &_NSConcreteGlobalBlock;
    //*******************


    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __blk_block_func_0(struct __blk_block_impl_0 *__cself) {

    printf("Global Block\n");
}

static struct __blk_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __blk_block_desc_0_DATA = { 0, sizeof(struct __blk_block_impl_0)};
static __blk_block_impl_0 __global_blk_block_impl_0((void *)__blk_block_func_0, &__blk_block_desc_0_DATA);
void (*blk)(void) = ((void (*)())&__global_blk_block_impl_0);

int main(int argc, const char * argv[]) {

    return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };

通过对比发现, 该Block的类为_NSConcreteGlobalBlock 类。此处的Block 即该Block用于结构体实例设置在程序的数据区域中。如下:

   //*******************
    impl.isa = &_NSConcreteGlobalBlock;
    //*******************

1)因为使用的是全局变量,所以,不能使用自动变量(局部变量),所以不存在对自动变量进行截获
2)由此Block用结构体实例的内容不依赖执行时的状态【第一点所述】,所以整个程序只需要一个实例。因此,将Block用结构体实例设置在与全局变量相同的数据区域中即可

  • 继续往下看

截获自动变量、不截获自动变量

//全局的block
typedef int(^blk_t)(int);

int main(int argc, const char * argv[]) {

    //截获自动变量的值都不同
    for (int rate = 0; rate < 10; ++rate) {
        blk_t blk = ^(int count) {
            return rate * count;
        };
    }

    //截获自动变量的值都相同
    for (int rate = 0; rate < 10; ++rate) {
        blk_t blk = ^(int count) {
            return count * rate;
        };
    }
  
    return 0;
}

他们的.cpp 代码都一样【 impl.isa = &_NSConcreteStackBlock;

typedef int(*blk_t)(int);


struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int rate;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _rate, int flags=0) : rate(_rate) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

对比发现

使用全局的Block,只要不截获自动变量,就可以将Block用结构体实例设置在程序的数据区域
使用全局的Block语法时,Block语法的表达式中不使用应截获的自动变量

上面分析了 数据区域的情况,那么 呢?

我们知道,栈是不需要手动管理内存的,会随着作用域的结束而结束,栈上的Block也一样,当其所属的变量作用域结束,该Block就被废弃,同样的,__block 变量也就被废弃
正因为如此,Block提供了一个方法解决这个问题: 把 Block 从栈复制到堆

1)将Block作为函数的返回值返回时,编译器会自动生成复制到堆上
2)执行copy方法

__block 变量存储域

block 从栈复制到堆时对__block变量产生的影响

__block变量的配置存储域 block 从栈复制到堆时的影响
从栈复制到堆并被Block持有
被Block持有

这个内存管理与OC的引用计数方式完全相同:使用__block 变量的Block 持有__block变量,如果Block被废弃,它所持有的__block变量也就被释放

利用copy方法复制使用了__block变量的Block语法。Block 和__block变量两者均时是从栈复制到堆

Block 知识_第2张图片
屏幕快照 2019-01-14 22.33.15.png

截获对象

先查看代码

typedef void(^blk_t)(id obj);


- (void)getBlockArray
{

    blk_t blk;
    {
        NSMutableArray *array = [[NSMutableArray alloc] init];
        blk = [^(id obj) {
            [array addObject:obj];
            NSLog(@"array count = %ld",array.count);
        } copy];
    }
    
    blk([NSObject new]);
    blk([NSObject new]);
    blk([NSObject new]);

}

输出如下:

2019-01-14 22:40:53.934087+0800 BlockDemo[3764:137369] array count = 1
2019-01-14 22:40:53.934265+0800 BlockDemo[3764:137369] array count = 2
2019-01-14 22:40:53.934361+0800 BlockDemo[3764:137369] array count = 3

为社么会出现如下的结果呢?
数组array 本应该在作用域结束的同时,变量array 被销毁,但是为什么代码却能正常运行,为什么呢?

通过.cpp 源码来研究


typedef void(*blk_t)(id obj);


struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  NSMutableArray *array;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSMutableArray *_array, int flags=0) : array(_array) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself, id obj) {
  NSMutableArray *array = __cself->array; // bound by copy

            ((void (*)(id, SEL, ObjectType _Nonnull))(void *)objc_msgSend)((id)array, sel_registerName("addObject:"), (id)obj);
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_jw_5qlq_4rj54j7dqfnvnfn_lq40000gn_T_main_c0f542_mi_0,((NSUInteger (*)(id, SEL))(void *)objc_msgSend)((id)array, sel_registerName("count")));
        }
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->array, (void*)src->array, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->array, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
  void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
int main(int argc, const char * argv[]) {

    blk_t blk;
    {
        NSMutableArray *array = ((NSMutableArray *(*)(id, SEL))(void *)objc_msgSend)((id)((NSMutableArray *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSMutableArray"), sel_registerName("alloc")), sel_registerName("init"));
        blk = (blk_t)((id (*)(id, SEL))(void *)objc_msgSend)((id)((void (*)(id))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, array, 570425344)), sel_registerName("copy"));
    }

    ((void (*)(__block_impl *, id))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk, ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("new")));
    ((void (*)(__block_impl *, id))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk, ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("new")));
    ((void (*)(__block_impl *, id))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk, ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("new")));

    return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };

发现 结构体__main_block_impl_0 中 NSMutableArray *array;, 成员变量使用__strong 修饰的【NSMutableArray *array,默认就是__strong】

在看源码中的

static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->array, (void*)src->array, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->array, 3/*BLOCK_FIELD_IS_OBJECT*/);}

因为结构体__main_block_impl_0 中 NSMutableArray *array;, 成员变量使用__strong 修饰的,所以需要恰当管理变量array对象,所以引入 函数__main_block_copy_0【相当于retain】和 __main_block_dispose_0【相当于release】,使用__strong 修饰的变量会被Block retain,随着Block的释放而释放

由此可知: Block使用的赋值给附有__strong修饰符的自动变量的对象和复制到堆上的__block变量由于被堆上的Block所持有,因而可超出其变量作用域而存在
简单的说:如果block 中使用附有__strong 修饰符的对象类型自动变量,,如果Block语法复制在成员变量blk中,那么当block从栈复制到堆时,该对象为Block所持有,这样容易引起循环引用

继续看下面两个方法,打印的结果 block都在堆上

 (void)test02 {
    
    //需要MRC模式 [-fno-objc-arc]
    void(^testTwoBlock)(void) = ^(){
    };
    NSLog(@"testTwoBlock=%@",testTwoBlock);
    //控制台输出
    //2018-08-09 15:27:50.244199+0800 BlockDemo[44164:838559] testTwoBlock=<__NSMallocBlock__: 0x60000004b8b0>      [ARC 环境下]
    
    //2018-08-09 15:32:35.558669+0800 BlockDemo[44738:861130] testTwoBlock=<__NSStackBlock__: 0x7ffee3239a80>       [MRC 环境下]
    //栈区block,函数调用完毕就会销毁
    
}


- (void)test03 {
    
    //NSMallocBlock:保存在堆中的block,此类型blcok是用copy修饰出来的block,它会随着对象的销毁而销毁,只要对象不销毁,我们就可以调用的到在堆中的block。
    self.block1 = ^(NSString *str, UIColor *color){

    };
    NSLog(@"block1=%@",self.block1);
    //控制台输出
    //2018-08-09 15:36:28.530531+0800 BlockDemo[45196:879447] block1=<__NSMallocBlock__: 0x600000250860>
    //用copy修饰的不会函数调用完就结束,随对象销毁才销毁,这种是在开发中正确使用block的姿势
    //block在有些情况下会造成block的循环引用
}

  • 总结:什么时候栈上的block会复制到堆上
    1、调用Block的copy方法
    2、Block作为函数的返回值返回时
    3、将Block赋值给 附有__strong修饰符id类型的类或者赋值给Block类型成员变量时【如上两个方法】

你可能感兴趣的:(Block 知识)