iOS Block完全使用指南

作者: 木头Lee | 来源:发表于2016-08-10 08:31 被阅读704次

    1、Block的声明

    格式:

    返回值 (^Block名称) (参数类型);
    

    举例:

        //无参无返回值的Block
        void (^Block01)();
    
        //有参无返回值的Block
        void (^Block02)(int);
    
        //无参有返回值的Block
        int (^Blcok03)();
    
        //有参有返回值的Block
        int (^Blcok04)(NSString*, NSString *);
    

    2、Block的定义

    格式:

      返回值类型 (^Block名称) (参数类型) = ^(参数) {
             代码体
        };
    

    举例:

    //定义无参无返回值的Block
    void (^Block01)() = ^(){
            NSLog(@"无参无返回值的Block");
        };
    
    //定义有参无返回值的Block
    void (^Block02)(NSString *) = ^(NSString * name) {
            NSLog(@"%@",name);
        };
    
    //定义有返回值无参Block(如果没有参数,=后面()可以省略)
    int (^Block03) () = ^() {   
            return 88;
        };
    
    //定义有参有返回值的Block
    int (^Block04)(int,int) = ^(int a, int b) {
            return a + b;
        };
    

    Tips:快速生成Block定义:在方法体内输入inlineBlock,选中提示,就会自动生成

    3、Block的调用

        //调用无参无返回值Block
        Block01();
        
        //调用有参无返回值Block
        Block02(@"lee");
        
        //调用无参有返回值Block
        int a = Block03();
        
        //调用有参有返回值Block
        int b = Block04(66,88);
    

    4、Block使用场景

    • 保存代码
    • 在一个方法中定义,在另一个方法中使用
      • 这样就需要将Block声明为成员属性
    方法1:
    @property (nonatomic,strong) int (^Block)(int ,int);      //Block怎么声明就怎么定义成属性
                                        -
    方法2:给Block类型起个别名
    typedef void (^BlockType) ();         //BlockType:Block类型(有无参数无、有返回值等等)
    @property (nonatomic,strong) BlcokType block;
    
    • 然后给成员属性赋值
    self.Block = ^(int a, int b) {
            return a + b;
        };
    
    • 在别的方法中直接调用成员属性的Block即可
    NSLog(@"%d",self.Block(66,88));
    

    • 在一个类中定义,在另一个类中使用(常用传值)

    需求:A控制器跳转到B控制器,当点击B控制器view的时候,让B控制器传一个字符串回A,并dismiss B控制器

    - 第一步:在B控制器声明一个Block属性
    
    /** Block属性 */
    @property(nonatomic,strong) void (^ Block) (NSString *);
    
    • 第二步:在跳转到B控制器之前,给B控制器的Block属性赋值(定义B控制器的Block)
    //点击按钮跳转到下一个控制器
    -(IBAction)nextVCButtonClick
    {   
            LGTestVC * testVC = [[LGTestVC alloc] init];
            testVC.Block = ^(NSString * name,UIViewController * VC){
                NSLog(@"%@",name);
                [VC dismissViewControllerAnimated:YES completion:nil];
            };
            [self presentViewController:testVC animated:YES completion:nil];
    }
    
    • 在B控制器调用Block即可传值(做事情)
    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
    {
        if (_Block != nil)
        {
            _Block(@"lee",self);
        }
    }
    

    • 作为方法参数使用

    注意:Block当做参数来使用,并不是马上调用;要在Block做什么事情由外部决定,什么时候做事情由方法内部决定。举个比较常用的例子:

        self.view.transform = CGAffineTransformMakeScale(0.5, 0.5);
        [UIView animateWithDuration:1 animations:^{
            [self.view layoutIfNeeded];
        }];
    
    • 作为方法返回值使用(多用于框架封装)

    Blcok作为返回值使用多用于框架封装,比较典型的一个例子就是:Masnoary, 最大的特点就是可以很方便的使用点语法,也就是链式编程。具体的Block作为返回值使用请参考摩卡奇童鞋的这篇文章 ,在此不再赘述

    5、Block内存管理

    • MRC:
    • 如果Block没有引用外部局部变量,Block就保存在内存的全局区
    • 如果Block引用了外部局部变量,Block就保存在内存的栈内存区

    • Block只能使用copy,不能使用retain(使用retain还是保存在栈里面,会被销毁)

      注意:block可以访问局部变量,但是不能修改.如果想要修改局部变量,需要在变量前加__block

    • ARC

    • 如果Block没有引用外部局部变量,Block就保存在内存的全局区
    • 只要Block引用了外部局部变量,Block就保存在堆内存中
    • Block最好不要使用copy,应该使用strong

    6、解决循环引用

    Block循环引用原因:Block会对里面所有外部局部变量(强指针变量)进行一次强引用,从而导致循环引用。

    //需要将外部的强指针变量弱化
    
    __weak typeof(self) weakSelf = self;
    
    _block = ^{
            
            NSLog(@"%@",weakSelf);
            
        };
    

    如果Block里面有非即时操作(延时操作),由于Block内部是弱指针指向,当{}结束之后就被销毁,从而导致延时操作没办法执行,需要在Block内部将弱化的指针再变为强指针

    __weak typeof(self) weakSelf = self;
    
    _block = ^{
            
            NSLog(@"%@",weakSelf);
    
           //将已经弱化的self指针,转换为强指针以便进行延时操作,否则就执行不到延时操作
            __strong typeof(weakSelf) strongSelf = weakSelf;
            
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                
                NSLog(@"%@",strongSelf);
            });
            
        };
    

    Tips:以下4种情况打印结果分别是多少?

    情况1:
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        
        int a = 3;
        void (^ block)() = ^{ 
            NSLog(@"%d",a);   
        };
        a = 5;
    
        block();
    }
    ————————————————————————————————————
    情况2:
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        
       static int a = 3;                    ***区别点:静态变量***
        void (^ block)() = ^{ 
            NSLog(@"%d",a);   
        };
        a = 5;
    
        block();
    }
    ————————————————————————————————————
    情况3:
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        
       __block int a = 3;                    ***区别点:被__block修饰***
        void (^ block)() = ^{ 
            NSLog(@"%d",a);   
        };
        a = 5;
    
        block();
    }
    ————————————————————————————————————
    情况4:
     __block int a = 3;                      ***区别点:全局变量***
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        
        void (^ block)() = ^{ 
            NSLog(@"%d",a);   
        };
        a = 5;
    
        block();
    }
    

    总结:

    • 如果是局部变量(情况1),Block是值传递,打印结果是3;
    • 如果是静态变量(情况2),全局变量(情况4),被__block修饰(情况3),Block是指针传递(地址传递),打印结果是5;

    相关文章

      网友评论

      • 最初的你我:同问。我记得我看的代码很多都是些copy
        最初的你我:@木头Lee 好的,谢谢你~~~
        木头Lee:@spirit_jsb 嗯,在ARC模式下strong和copy都可以,在MRC模式必须要用copy :smiley:
      • 马爷:楼主 为什么是 strong? 为什么不能使用 copy 有什么利弊吗?
        木头Lee:@sandouchan 正解, :+1: 老程序员习惯用copy也是因为之前在MRC模式下的使用习惯,其实在ARC模式下strong和copy都一样
        木头Lee:@马爷 不好意思,刚看到~ 在非ARC模式下开发必须要用copy,在ARC模式下用strong和copy都是一样的,系统会自动retain和release :pray:
        ff6250868c5d:@马爷 博主这句话有点误导性,其实Block用strong或copy都没问题,这个问题也没人能说出区别,很多老程序员都习惯用copy,甚至苹果官方也是用copy,所以跟着苹果走是没错的~

      本文标题:iOS Block完全使用指南

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