OC中的Block(三)

block的类型

block有3种类型,可以通过调用class方法或者isa指针查看具体类型,最终都是继承自NSBlock类型

  • __NSGlobalBlock__ (_NSConcreteGlobalBlock )
  • __NSStackBlock__ (_NSConcreteStackBlock )
  • __NSMallocBlock__ (_NSConcreteMallocBlock )
应用程序内存分配
程序区域 .text区
数据区域 .data区
堆区
栈区
不同类型block的存放区域

生成不同类型的block

block类型 环境
NSGlobalBlock(数据区) 没有访问auto变量
NSStackBlock(栈区) 访问了auto变量
NSMallocBlock(堆区) NSStackBlock调用了copy

每一种类型的block调用copy后的结果如下所示:

block类型 副本源的配置存储域 复制效果
NSGlobalBlock(数据区) 栈区 从栈复制到堆区
NSStackBlock(栈区) 程序的数据区 什么也不做
NSMallocBlock(堆区) 堆区 引用计数增加

block的copy

在ARC环境下,编译器会根据情况自动将栈上的block复制到堆上,比如以下情况

  • block作为函数返回值时
  • 将block赋值给__strong指针时
  • block作为Cocoa API中方法名含有usingBlock的方法参数时
  • block作为GCD API的方法参数时

MRC下block属性的建议写法

@property (copy, nonatomic) void (^block)(void);`

ARC下block属性的建议写法

@property (strong, nonatomic) void (^block)(void);
@property (copy, nonatomic) void (^block)(void);

对象类型的auto变量

当block内部访问了对象类型的auto变量时

  • 如果block是在栈上,将不会对auto变量产生强引用

  • 如果block被拷贝到堆上

  1. 如果block被拷贝到堆上
  2. copy函数内部会调用_Block_object_assign函数
  3. _Block_object_assign函数会根据auto变量的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用
函数 点用时机
__main_block_copy_0 栈上的block复制到堆时
__main_block_dispose_0 堆上的block被废弃时
//block被拷贝到堆上时调用
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {

//会根据auto变量的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用
_Block_object_assign((void*)&dst->obj,
                         (void*)src->obj,
                         8/*BLOCK_FIELD_IS_BYREF*/);
}

//block从堆上移除时调用
static void __main_block_dispose_0(struct __main_block_impl_0*src) {
//会自动释放引用的auto变量(release)
_Block_object_dispose((void*)src->obj,
                          8/*BLOCK_FIELD_IS_BYREF*/);
}

__weak修饰符

__weak修饰符可以是block生成的结构体中捕获的变量为__weak变量..则可以对变量进行弱引用,解决循环引用问题

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  //捕获__weak变量
  NSString __weak *name;
  .
  .
  .
};

在使用clang转换OC为C++代码时,可能会遇到以下问题

cannot create __weak reference in file using manual reference

解决方案:支持ARC、指定运行时系统版本,比如

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m

__block修饰符

__block可以用于解决block内部无法修改auto变量值的问题.但是__block不能修饰全局变量、静态变量(static)

编译器会将__block变量包装成一个对象

__block int age = 13;
^{
    NSLog(@"%d", age);
}();

会生成一下C++代码:

struct __main_block_impl_1 {
  struct __block_impl impl;
  struct __main_block_desc_1* Desc;
  __Block_byref_age_2 *age; // by ref
  __main_block_impl_1(void *fp, struct __main_block_desc_1 *desc, __Block_byref_age_2 *_age, int flags=0) : age(_age->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
struct __Block_byref_age_2 {
  void *__isa;
__Block_byref_age_2 *__forwarding;
 int __flags;
 int __size;
 int age;
};

你可能感兴趣的:(OC中的Block(三))