美文网首页IOS收藏
从应用的角度看block(待续)

从应用的角度看block(待续)

作者: 一声雷 | 来源:发表于2016-02-12 16:39 被阅读47次

    一、什么是block?

    按照苹果官方文档的说法,block是一个oc对象。(原文:Blocks are Objective-C objects, which means they can be added to collections like NSArray or NSDictionary. )其实,本质就是一个函数。
    当然,block也可以理解成oc的一种数据类型,我个人认为把block理解成一种数据类型更便于我们理解和记忆。下面我就从这个角度详细介绍block。

    二、block的介绍

    我们在定义一个变量的时候,都包含了以下三个步骤:
    1、确定这个变量的数据类型
    2、声明变量
    3、定义变量

    // 1.比如确定这个数据是 int 类型
    int  value ;         // 2.声明
    value = 15 ;         // 3.定义
    
    
    // 当然,也可以声明的同时进行定义
    int value = 15 ;
    

    从代码中认识block

    例如:
    //   1.一个有参数有返回值的block类型:int (^) (int ,int)
    int (^block1) (int ,int);           // 2.block的声明
    block1 = ^int(int n1,int n2){       // 3.block的定义
        int  sum = n1 + n2;
        return  sum;
    }
    block1(10,20);                      // 4.block的调用
    
    
    // 当然,也可以声明的同时进行定义
    void(^block2)(int) = ^(int a){ 
               NSLog(@"你输入的值是:%d",a );    
         };
    block2(999);
    // 我们可以看出block2的类型是:void(^)(int)
    
    

    1.block的类型

    格式:返回值类型(^)(block参数类型)
    block的类型大致可以分为四种:
    a: 无参数,无返回值
    b: 无参数,有返回值
    c: 有参数,无返回值
    d: 有参数,有返回值

    2.block的声明

    格式:**返回值类型(^Block变量名)(block参数类型 参数变量名) **

    int (^block11)(int a,int b);
    int (^block22)(int,int);             // 注意:参数变量名可以省略
    

    3.block的定义

    格式:^返回值类型(参数类型 参数变量名) { // 代码块 } ;

    // 注意1:声明的时候参数的变量名可以省略,当定义是参数的变量名一定不能省略
      void(^block111)(int) = ^void(int a) {           
        };
      int(^block222)(int) = ^int(int a) { 
           return 2;   
        };  
    // 注意2:返回值的类型是可以省略的
       void(^block111)(int a) = ^(int a) {           
      };
      int(^block222)(int a) = ^(int a) { 
           return 2;   
       };  
     
    // 注意3: 当没有返回值,没有参数时,可以省略括号
        void(^block333)() = ^{       
        };
    

    4.block的调用

    格式:block变量名(参数);

    5.如何声明一个block属性

    方式一:

    @interface ViewController ()
    @property (nonatomic, strong) void(^block)();         // 方式一
    @end
    

    方式二:

    typedef  void(^BlockType)();           // BlockType:block类型别名
    @interface ViewController ()
    @property (nonatomic, assign) BlockType block1;      // 方式二
    @end
    

    三、block的内存管理

    我们都知道内存是可以划分成堆,栈,方法区,常量区,全局区5个区。而block是可能被存放在以下三个区中的任意一个中。

    1.在全局静态区:_NSConcreteGlobalBlock
    2.在栈中:_NSConcreteStackBlock
    3.在堆中:_NSConcreteMallocBlock

    MRC中:
    1.如果block没有访问外部的局部变量或者局部变量被static修饰,block默认存放在"全局区"
    2.如果block访问外部的局部变量,block存放在"栈"里面

      MRC:不能使用retain声明block,依然放在栈里面,会自动销毁.
      MRC:使用copy声明block,才会放在堆里面.
    

    ARC如何管理Block:
    1.如果block没有访问外部的局部变量或者局部变量被static修饰,block默认存放在"全局区"
    2.如果block访问外部的局部变量,block存放在"堆"里面

      ARC:使用strong声明block,不要使用weak.
    

    四、block的循环引用问题

    ------------“你强我就强,你弱我就弱”------------

    在block的内部访问了一个对象:
    1.如果这个对象本来就有强指针指着,那么这个block也会强引用这个对象;
    2.如果这个对象只有弱指针引着,那么这个block就不会对这个对象进行强引用。

    Person类:

    
    
    #import <Foundation/Foundation.h>
    
    @interface Person : NSObject
    @property(nonatomic,strong)NSString *name;  // 姓名
    @property(nonatomic,assign)int age;         // 年龄
    @property(nonatomic,assign)int height;      // 体重
    // 声明block
    @property(nonatomic,strong) void(^block)();
    - (void)eat;
    @end
    
    @implementation Person
    - (void)eat
    {
        self.height += 1 ;
        _block = ^{
              // 在block的内部访问了自身这个对象。
            NSLog(@"吃完饭后的体重是:%d",self.height);
        };
        _block();
    }
    @end
    

    Person类外部使用:

    // 这时候,p1这个强指针引着Person对象。
    Person *p1 = [[Person alloc] init];
    // eat内部调用block时,block就会强引用这person对象;
    // 而本身person对象内部就强引着block。
    [p1 eat];
    // person对象和block两者互相强引着,谁也不能释放。
    

    解决方案:

    
    
    - (void)eat
    {
        self.height += 1 ;
        //声明weakSelf弱指针(解决方案)
        __weak typeof(self) weakSelf = self;
        _block = ^{
            NSLog(@"吃完饭后的体重是:%d",weakSelf.height);
        };
        _block();
    }
    

    更为复杂的情况:🏁 延迟操作或者异步任务 🏁

    
    - (void)viewDidLoad 
        // 声明weakSelf弱指针   
     __weak typeof(self) weakSelf = self;       
    _block = ^{
                
               //因为这里如果不用强指针的话,延时后,可能访问的这个对象就会销毁了,那么这个block的内部就没办法访问这个对象了
               //这样用强指针引着,就保证了只要block还在,那么这个对象就不会销毁
                __strong typeof(weakSelf) strongSelf = weakSelf; 
                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                          NSLog(@"%@",strongSelf);                
                         });           
              };      
    
     _block();
       
    }
    

    相关文章

      网友评论

        本文标题:从应用的角度看block(待续)

        本文链接:https://www.haomeiwen.com/subject/wbzykttx.html