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;
};
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容