Block

248 阅读3分钟
  • block本质上也是一个OC对象,它内部也有个isa指针
  • block是封装了函数调用以及函数调用环境的OC对象
  • block是封装函数及其上下文的OC对象

block 语法:

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

^ (NSString *) (NSString *bookId){}

省略返回值写法:

^(void){};

省略返回值及参数写法:

^{};

Block定义声明使用

//声明
typedef void (^Block)();
typedef int (^MyBlock)(int , int);
typedef void(^ConfirmBlock)(BOOL isOK);
typedef void(^AlertBlock)(NSInteger alertTag);

//定义属性
@property (nonatomic,copy) MyBlock myBlockOne;

//使用
self.myBlockOne = ^int (int ,int){
    //TODO
}

image.png

Block的类型

A. 全局Block(_NSConcreteGlobalBlock)

  • 数据区域(.data区)
  • 在block内部不会访问任何外部变量,或者只使用静态变量和全局变量,执行完就销毁。
- (void)GlobalBlock{
    void (^blk)(void) = ^ {
    };
    blk();
    NSLog(@"blk>>>>:%@",blk);
}

image.png

/** 全局变量 */
int global_count = 10;
/** 静态全局变量 */
static int static_global_count = 10;

int main(int argc, const char * argv[]) {
    
    /** 静态局部变量 */
    static int static_count = 10;
    
    void (^block)(void) = ^ {
        global_count = 11;
        static_global_count = 11;
        static_count = 11;
    };
    
    block();
    return 0;
}

image.png

B. 堆Block(_NSConcreteMallocBlock)

  • 位于堆区
  • 在Block内部使用局部变量或者OC的属性,并且赋值给强引用或者Copy修饰的变量
- (void)MallocBlock{
    int val = 10;
    void (^blk2)(void) = ^ {
        NSLog(@">>>>:%@",@(val));
    };
    blk2();
    NSLog(@">>>>:%@",blk2);
}

image.png

C.栈Block(_NSConcreteStackBlock)

  • 位于栈区,栈内有效,出栈后销毁。
  • 与MallocBlock一样,可以在内部使用局部变量或者OC的属性。但是不能赋值给强引用或者Copy修饰的变量
  • 在ARC环境下,捕获局部变量、成员变量,且没有被强引用的block都是_NSConcreteStackBlock
捕获局部变量
- (void)StackBlock{
   /** 局部变量 */
    int count = 10;
    /** 
        执行 
        或者使用void (^__weak block)(void)来指向block
    */
    ^{
        NSLog(@"%d", count);
    }();
    // 打印Block对象
    NSLog(@"blk25>>>>:%@", ^{
        NSLog(@"%d", count);
    });
}

image.png

捕获成员变量
@interface ViewController ()

@property (nonatomic, assign) int count;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];    
    NSLog(@"Block对象:%@", ^{
        NSLog(@"%d", self.count);
    });

    ^{
        NSLog(@"%d", self.count);
    }();
}
@end

但是在ARC下,很少存在栈上的block。因为很多情况下ARC会主动帮你把block copy一次。 举个例子:

__block int static_k = 3;
void (^myBlock)(void) = ^{
    static_k++;
};
myBlock();
NSLog(@"%@",myBlock);

在MRC下,输出结果为__NSStackBlock栈。 在ARC下,输出结果为__NSMallocBlock堆。

因为在block在捕获外部变量时ARC会自动帮我们把栈上的block copy到堆上,其中还包括下面的情况系统也会主动copy一次。 以下情况会被保存在堆上: 1.手动调用copy 2.Block是函数的返回值 3.Block被强引用,Block被赋值给__strong或者id类型 4.调用系统API入参中含有usingBlcok的方法

但并不是说在ARC上就没有栈上的block。其中当Block为函数参数的时候,就需要我们手动的copy一份到堆上了。其中GCD等系统方法中本身带usingBlock的方法,不需要处理。

三种block的拷贝效果

不多说,直接上表格:

Block的类副本源的配置存储域赋值效果
_NSConcreteGlobalBlock数据区什么也不做
_NSConcreteStackBlock从栈拷贝到堆
_NSConcreteMallocBlock引用计数增加
数据区中的_NSConcreteGlobalBlock执行拷贝操作什么也不做的原因是不需要,因为_NSConcreteGlobalBlock在进程结束后才销毁,已经是广域变量了。