八、Block

一、Block

1.Block的基本概念

block是一种特殊的数据类型,可以保存一段代码在合适的时候调用。
功能相当于函数和方法。
区别: 1. 函数不能嵌套定义,block可以;
2. 函数作为形参或返回值必须是以指针的形式,block作为数据类型可以直接传。因此block比函数更加强大

2.Block的格式

三部分:block的声明、定义和调用;

  1. 声明: 返回值类型 (^block名称) (形参列表);
    int (^Myblock) (int);
  2. 定义:block名称 = ^(形参列表){ … }; MyBlock = ^(int num1){...};
  3. 调用:block名称( ); MyBlock( );
    注:定义的同时初始化
    int (^sumBlock)(int,int) = ^(int num1,int num2){ return num1+num2};

3.Block的应用场景

typedef定义Block

typedef int(^calculate)(int,int);   // typedef给Blcok起别名,别名即为Block名称。也是Block 的类型。
calculate sum = ^(int num1,int num2){ return num1+num2};
NSLog(@"sum  = %d",sum(10,20));

应用场景:动画、多线程、网络请求回调等。
block作为数据类型,可以作为形参、返回值;
当代码复用时如果前后相同,中间不同时,可以使用block作为参数来简化代码。

4.Block的注意事项

  1. Block 可以访问外部变量的值;
int a = 10;
void (^MyBlock)() = ^{
 NSLog(@"a = %d",a);   //a = 10;
};
MyBlock();    
  1. Block内部可以定义和外部同名的变量,访问的是block内部的变量;
int a = 10;
void (^MyBlock)() = ^{
 int a=20;
 NSLog(@"a = %d",a);   //a = 20;
};
MyBlock();    
  1. 默认情况下,Block不可以修改外部变量的值
    因为Block内部使用外部定义的变量时,如果没有使用__block修饰,block内部的变量是readonly,因此是可读不可写。
int a = 10;
void (^MyBlock)() = ^{
 a=20;
 NSLog(@"a = %d",a);   // 报错:variable isnot assignable(missing __block type specifier)
};
MyBlock();    
  1. 为什么基本数据在block内部不能修改外部变量的值?加了__block后可以修改外部变量的值?
__block  int a = 10;
void (^MyBlock)() = ^{
    a=20
 NSLog(@"a = %d",a);     // a=20
};
MyBlock();    

注:基本数据类型是值传递无法修改,而__block后变量是指针传递可以修改。

总结:
1)当是基本数据类型或对象类型,值传递。Block内部不可以修改外部变量的值;
2)当使用静态变量static全局变量__Block修饰的对象,指针传递。可修改外部变量的值。
3)何时使用__Block?
答:当外部变量是基本数据类型或对象,而Block内部想要修改或赋值外部变量时,可使用。但注意是修改或赋值时,使用不算。
在这里插入图片描述 =》需要,此处为赋值。
在这里插入图片描述=》不需要,不是赋值不是修改;

二.Block的内存管理

Block在内存中分为全局block、堆Block、栈Block;

1. Block存储位置

当Block未使用外部变量时,Block是放在全局区;
当Block访问的外部变量是由static、全局变量修饰,Block仍然放在全局区;
当Block在MRC下,默认是放在栈中;
当Block在ARC下,默认放在堆中。

2. Block在MRC下

当Block在MRC下,Block默认存储在栈中。当对Block使用 copy操作时,Block会转移到堆中。
如果Block在中,block访问外部对象, 不会对对象进行retain 操作;
如果Block在中,Block访问外部对象, 对对象进行retain操作;

问1:Block在MRC下使用copy进行操作后,会造成内存泄漏吗?
答:会,因为Block使用copy后,将block从栈转移到堆中。当访问外部对象,会对对象进行retain,如果不进行release就会造成内存泄漏。
注:因此如果访问外部对象:一定使用__block修饰对象,这样即使block在堆中也不会对对象进行retain。

问2:Block作为属性使用什么进行修饰?为什么?
答:使用copy,因为在MRC下只有copy才能将Block从栈转移到堆中,避免代码块过掉对象被释放。而ARC中局部变量默认是强指针放在堆中,所以还是使用copy进行修饰。

3. Block在ARC下

当Block在ARC下,没有访问外部变量,Block默认放在全局区。
访问了外部变量,Block默认放在堆中。
问:ARC下使用copy还是strong? 可以使用weak吗?
答:不可以使用weak,因为ARC下局部变量默认强指针引用。如果改成弱指针则过了作用域,再去访问会造成坏访问的问题;
可以使用copy或strong,三方框架为了沿袭传统使用copy,当然也可以使用strong修饰;

四.Block在实际开发中的常用方式

1. Block用于模型,保存一段代码

在定义模型时,为了保证模型在创建时具备某种功能,可以使用block作为参数来实现。 比较常见:AFN网络请求…

 // carItem.h
 @property(noatomic,strong)void(^carBlcok)();  // 首先要添加一个block属性
 +(instancetype)itemWithName:(NSString *)name carBlock:(void(^)())block; //定义一个快速方法,block做参数
 
  // carItem.m
 +(instancetype)itemWithName:(NSString *)name carBlock:(void(^)())block
    {
    CarItem *item=[[self alloc]init];
    item.name = name;
    item.carBlock = block;
    return item;
    }
    
// carTableViewController.m
-(void)viewDidLoad{
    [super viewDidLoad];
    CellItem *item = [CellItem itemWithName:@"BWM" carBlock:^{
    NSLog(@"name = %@", item.name);    // 添加功能
 }]
}

2. Block作为参数进行传递

如:定义一个计算类,block作为参数传递;

  // caculatorManager.h
  @property(noatomic,assign)NSInteger result;  // 定义一个Block调用时的传参属性
  -(void)caculator:(NSInteger(^)(NSInteger))caculatorBlock //,block做参数
  
   //  caculatorManager.m
   -(void)caculator:(NSInteger(^)(NSInteger))caculatorBlock{
     if(caculatorBlock){
       caculatorBlock(_result);               //何时运算由内部决定:caculator本类
    }
  }
  
     //  ViewController.m
     -(void)ViewDidLoad{
      caculatorManager *mgr =[[caculatorManager alloc]init];
      [mgr caculator:^NSInteger(NSInteger result){    //如何计算由外部决定:除caculator以外的调用类
          result ++;
          return result;
      }];
  }

重点:因此Block作为参数最重要的是:如何计算由外部决定,何时计算由内部决定; 和普通的调用正好相反。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值