block

一、概述

block又称为闭包,一个能够访问局部变量的函数。
闭包=一个函数(或指向函数的指针)+该函数执行的外部上下文变量(自由变量)

block:
1、可嵌套定义;
2、可定义在方法的内部或外部;
3、本质是对象,使代码高内聚。

使用clang命令将OC代码转换为C++文件。
命令如下:

clang -rewrite-objc main.m

在同级目录下生产了一个同名的.cpp文件,打开查看:

open main.cpp
二、block的定义与使用

1、无参数返回值

void (^MyBlockOne)(void) = ^(void){
    NSLog(@"无参数,无参数返回值");
};
MyBlockOne();//无参数

2、有参数无返回值 也可写为void (^MyBlockTwo)(int value)

void (^MyBlockTwo)(int) = ^(int value){
    NSLog(@"有参数无返回值:%d",value);
};
MyBlockTwo(1);//有参数

3、无参数有返回值

int (^MyblockThree)(void) = ^(void){
    return 1;
};
int result = MyblockThree();
NSLog(@"无参数参数有返回值:%d",result);

4、有参数有返回值

int (^MyblockFour)(int ,int) = ^(int a, int b){
    return a+b;
};
result = MyblockFour(1,3);
NSLog(@"有参数参数有返回值:%d",result);

5、项目中经常上使用typedef定义block定义后可以声明为一个block属性,copy修饰,即使非copy修饰系统也会自动对该属性进行copy操作

typedef int (^Myblock)(int a,int b);
@property (nonatomic,copy) Myblock block;
self.block = ^int(int a, int b) {
    NSLog(@"我是一个typedef定义的block,a=%d,b=%d",a,b);
    return a+b;
};
self.block(1,3);
三、block与外界变量

1、默认情况
block外的变量引用,默认是将其复制到block数据结构中实现访问的,没用直接引用变量本身,因此不能修改外部变量。

-(void)blockOne{
    int age = 18;
    void (^block)(void) = ^(void){
        NSLog(@"1、默认情况  age:%d",age);
    };
    age = 20;
    block();//打印结果为18
}

__block修饰外部变量
使用__block修饰的变量,其实复制了该变量的地址来实现访问的,因此在block内部或外部修改,该变量都将被修改。

-(void)blockTwo{
    __block int age = 18;
    void (^block)(void) = ^(void){
        NSLog(@"2、__block修饰外部变量 age:%d",age);
    };
    age = 20;
    block();//打印结果为20
}

使用命令将OC代码转换成C++文件:

clang -rewrite-objc External.m

查看代码可见blockTwo方法对应的转换后的C++代码:

static void _I_External_blockTwo(External * self, SEL _cmd) {
    __attribute__((__blocks__(byref))) __Block_byref_age_0 age = {(void*)0,(__Block_byref_age_0 *)&age, 0, sizeof(__Block_byref_age_0), 18};
    void (*block)(void) = ((void (*)())&__External__blockTwo_block_impl_0((void *)__External__blockTwo_block_func_0, &__External__blockTwo_block_desc_0_DATA, (__Block_byref_age_0 *)&age, 570425344));
    (age.__forwarding->age) = 20;
    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}

__block int age = 18转换成一个结构体:

struct __Block_byref_age_0 {
  void *__isa;//指向一个存储自身基础信息的地址的指针
__Block_byref_age_0 *__forwarding;//指向自己的指针
 int __flags;//标识
 int __size;//结构体大小
 int age;//变量值
};

对应以上的结构体赋值如下:

__Block_byref_age_0 age = {
    (void*)0,
    (__Block_byref_age_0 *)&age, 
    0, 
    sizeof(__Block_byref_age_0), 
    18
};
四、block存储区

block的内存分布有三种类型:
1、全局块(_NSConcreteGlobalBlock)
2、栈块(_NSConcreteStackBlock)
3、堆块(_NSConcreteMallocBlock)

确定block的存储位置:
1、block不访问外部变量(包括栈和堆中的变量)
block不在栈中也不在堆中,在代码代码段中,ARC、MRC下也是如此,此时为全局块。
2、block访问外界变量
MRC环境下:block默认存储在栈区。
ARC环境下:block默认存储在堆区,实际放在栈区,ARC情况下会自动拷贝到堆区,自动释放。
3、block代码块本身为栈区。

//1、全局区
Myblock block = ^int(int a, int b) {
    NSLog(@"我是一个typedef定义的block,a=%d,b=%d",a,b);
    return a+b;
};
block(1,3);
NSLog(@"全局区打印:%@",block);//NSGlobalBlock
//2、栈区-代码块即为栈区必须访问外部变量
int a = 10;
NSLog(@"栈区打印:%@",^{
    NSLog(@"栈区打印:%d",a);//NSStackBlock
});
//3、堆区
__block int b = 10;
void (^mallocBlock)(void) = ^(void){
    b = 1;//此处必须有引用才会被复制到堆区
};
NSLog(@"堆区打印:%@",mallocBlock);//NSMallocBlock

ARC下,访问外部变量的block为什么要自动从栈区拷贝到堆区,栈上的block当作用域结束,block就被释放,和自动变量一样,__block变量也同时被废弃,为延长生命周期,需要把block复制到堆中,复制完成原有block被释放,保留当前block块。

五、__block变量与_forwarding

copy操作后,栈区__forwarding,由指向自己变为指向复制后的堆上的结构体。

block.png

六、防止block循环引用

1、ARC下使用__weak修饰对象
2、MRC下使用__block修饰对象

1)__weak修饰对象解决循环引用
block中使用延时操作,self会在延时操作前释放,因此在使用延时操作时需要对weakSelf有个强引用才能在延时操作中使用。

-(void)demo1{
    self.name = @"hibo";
    __weak typeof (self) weakSelf = self;
    self.block1 = ^ {
        __strong typeof (self) strongSelf = weakSelf;
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"name:%@",strongSelf.name);
        });
        NSLog(@"1->%@",weakSelf.name);
    };
    self.block1();
}

但是出现一个奇怪的问题:

block.png

2)传参代替__weak

-(void)demo2{
    self.name = @"yahibo";
    self.block2 = ^ (ViewController *vc){
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"demo2:%@",vc.name);
        });
        NSLog(@"demo2->%@",vc.name);
    };
    self.block2(self);
}

3)__block应用对象,在block中置空解决循环引用:

-(void)demo3{
    self.name = @"yahibo";
    __block SecondController *vcself = self;
    self.block3 = ^{
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"demo3:%@",vcself.name);
            vcself = nil;
        });
    };
    self.block3();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值