Skip to content
Kevin775263419 edited this page Mar 12, 2019 · 1 revision

block的本质

  • block本质上也是一个OC对象,它内部也有个isa指针

  • block是封装了函数调用以及函数调用环境的OC对象

  • block的底层结构如右图所示,基本上都是结构体,这只是一个大概的结构

    isa flag reversed invoke descriptio

Capture(变量捕获)

  • 为了保证block内部能够正常访问外部的变量,block有个变量捕获机制

    变量类型 捕获到block内部 访问方式
    auto(局部) YES 值传递
    static(局部) YES 指针传递
    全局变量 NO 直接访问

Block的内存管理

block的类型

  1. _NSGlobalBlock: 全局block
  2. _NSStackBlock: 栈block
  3. __NSMallocBlock: 堆block

block的存储位置

block类型 环境 配置存储区域 copy效果
_NSGlobalBlock 没有访问Auto变量 程序的数据区域 什么也不做
_NSStackBlock 访问了Auto变量 栈区 从stack复制到heap
__NSMallocBlock __NSStackBlock__调用了copy 堆区 引用计数增加
  • block类型:block的类型
  • 环境:是否capture的变量
  • 配置存储区域:放在内存你的位置
  • copy:执行copy的效果
    • tips:NSStackBlock__调用了copy之后,就会变成MallocBlock,通过一个forwarding 指向栈区的block
    • forwarding存在的作用,不论在内存的任何位置可以访问同一个block

block的Copy

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

    • block作为函数返回值时
    • 将block赋值给__strong指针时
    • block作为Cocoa API中方法名含有usingBlock的方法参数时
    • block作为GCD API的方法参数时
  2. MRC下block属性的建议写法

    @property (copy, nonatomic) void (^block)(void);
  3. ARC下block属性的建议写法

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

block获取对象类型的auto变量类型

  • 当block内部访问了对象类型的auto变量时
    • 如果block是在栈上,将不会对auto变量产生强引用
    • 如果block被拷贝到堆上
      • 会调用block内部的copy函数
      • copy函数内部会调用_Block_object_assign函数
      • _Block_object_assign函数会根据auto变量的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用
    • 如果block从堆上移除
      • 会调用block内部的dispose函数
      • dispose函数内部会调用_Block_object_dispose函数
      • _Block_object_dispose函数会自动释放引用的auto变量(release)

__block修饰符

  • __block可以用于解决block内部无法修改auto变量值的问题

  • __block不能修饰全局变量、静态变量(static)

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

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

__block的内存管理

  • 当block在栈上时,并不会对__block变量产生强引用
  • 当block被copy到堆时
    • 会调用block内部的copy函数
    • copy函数内部会调用_Block_object_assign函数
    • _Block_object_assign函数会对__block变量形成强引用(retain)
  • 当block从堆中移除时
    • 会调用block内部的dispose函数
    • dispose函数内部会调用_Block_object_dispose函数
    • _Block_object_dispose函数会自动释放引用的__block变量(release)

__block的__forwarding指针

  1. 如果是stackBlock,forwarding指针指向自己。
  2. 当stackBlock复制到heapBlock中时,原来stack中的forwarding指针指向,heapBlock中的结构体

解决循环引用问题 - ARC

3种解决方案

正常情况下一般会有这3种方案解决

typedef void (^Block)(void);
@property (nonatomic, copy) Block block;
//解决方案
    //__weak
    __weak typeof (self) weakSelf = self;
    self.block = ^{
        printf("%p",weakSelf);
    };
    
    //__unsafe_unretained
    __unsafe_unretained id unretainSelf = self;
    self.block = ^{
        printf("%p",unretainSelf);
    };
    
    //__block(need invocation block)
    __block id blockSelf = self;
    self.block = ^{
        printf("%p",blockSelf);
        blockSelf = nil;
    };
    self.block();
  1. 用__weak 修饰
  2. 用__unsafe_unretained修饰
  3. 用__block修饰(注意:需要调用block,同时blockSelf需要滞空)

建议解决方案

建议使用YYKit库里面的封装

将下面这段代码复制到pch文件中,调用方法如下面的例子。比较简单好用

/**
 Synthsize a weak or strong reference.
 
 Example:
    @weakify(self)
    [self doSomething^{
        @strongify(self)
        if (!self) return;
        ...
    }];

 */
#ifndef weakify
    #if DEBUG
        #if __has_feature(objc_arc)
        #define weakify(object) autoreleasepool{} __weak __typeof__(object) weak##_##object = object;
        #else
        #define weakify(object) autoreleasepool{} __block __typeof__(object) block##_##object = object;
        #endif
    #else
        #if __has_feature(objc_arc)
        #define weakify(object) try{} @finally{} {} __weak __typeof__(object) weak##_##object = object;
        #else
        #define weakify(object) try{} @finally{} {} __block __typeof__(object) block##_##object = object;
        #endif
    #endif
#endif

#ifndef strongify
    #if DEBUG
        #if __has_feature(objc_arc)
        #define strongify(object) autoreleasepool{} __typeof__(object) object = weak##_##object;
        #else
        #define strongify(object) autoreleasepool{} __typeof__(object) object = block##_##object;
        #endif
    #else
        #if __has_feature(objc_arc)
        #define strongify(object) try{} @finally{} __typeof__(object) object = weak##_##object;
        #else
        #define strongify(object) try{} @finally{} __typeof__(object) object = block##_##object;
        #endif
    #endif
#endif