美文网首页
Block相关

Block相关

作者: CoderLNHui | 来源:发表于2017-01-13 13:55 被阅读26次

    Block基本使用

    • 1.1、block声明:
      返回值(^block变量名)(参数)

      void(^block)();

    • 1.2、block定义:三种方式 = ^(参数){};

    • 第一种

        void(^block1)() = ^{
    
            NSLog(@"调用了block1");
    
        };
    
    • 第二种 如果没有参数,参数可以隐藏,如果有参数,定义的时候,必须要写参数,而且必须要有参数变量名
        void(^block2)(int) = ^(int a){
        };
    
    • 第三种 block返回可以省略,不管有没有返回值,都可以省略
        int(^block3)() = ^int{
    
            return 3;
    
        };
    
    block类型:int(^)(NSString *)
    
        int(^block4)(NSString *) = ^(NSString *name){
    
            return 2;
    
        };
    

    1.3、block调用

    block1();
    

    1.4、block快捷方式 inline

    block开发使用场景(保存代码)

    • 2.1、block如何定义成属性

      • 2.1.1、block怎么声明,就如何定义成属性
        @property (nonatomic, strong) void(^block)();
      • 2.1.2、BlockType:类型别名
        typedef void(^BlockType)();
        @property (nonatomic, strong) BlockType block1;
    • block开发使用场景(传值)

    Block内存管理

    • 4.1、面试题:
      block是不是一个对象?是一个对象
    • 4.2、如何判断当前文件是MRC,还是ARC
      • 1.dealloc 能否调用super,只有MRC才能调用super
      • 2.能否使用retain,release.如果能用就是MRC
    • 4.3、block的内存管理分两种情况:MRC和ARC
      • 4.3.1、MRC了解开发常识:

        1.MRC没有strong,weak,局部变量对象就是相当于基本数据类型(基本数据类型放在栈里,代码块结束就销毁)
        2.MRC给成员属性赋值,一定要使用set方法,不能直接访问下划线成员属性赋值
        解释:在set方法里会做些额外的处理,如果用_name就不会有relase和retain操作,如:

            @property (nonatomic, retain) NSString *name;
             - (void)setName:(NSString *)name
        {
            if (name != _name) {
                [_name release];
                _name = [name retain];
            }
        }
        ```
        
    - 4.3.2、MRC:管理block
      -  1、只要Block引用外部局部变量,block放在栈里面.
     -  2、block只能使用copy,不能使用retain,使用retain,block还是在栈里面(代码块结束就销毁),而使用copy,block就在堆里面
    解释:
      > ```            
              1、
                __block int a = 3;
          void(^block)() = ^{ 
            NSLog(@"调用block%d",a);
        };
    

    此时block引用外部的局部变量,在栈里
    2、
    @property (nonatomic, copy) void(^block)();

      - MRC下:
    
    - (void)viewDidLoad {
    
        [super viewDidLoad];
    
        __block int a = 3;
    
        
    
        void(^block)() = ^{
    
            
    
            NSLog(@"调用block%d",a);
    
        };
    
        
    
        self.block = block;
    
    }
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
    
    {
    
        self.block();
    
        
    
    }
    
    • 4.3.3、ARC:管理block

    ARC管理原则:
    只要一个对象没有被强指针修饰就会被销毁,默认局部变量对象都是强指针,存放到堆里面
    ARC:管理block
    只要block引用外部局部变量,block放在堆里, block使用strong.最好不要使用copy

    • ARC下:
    @property (nonatomic, strong) void(^block)();
    
    //@property (nonatomic, copy) void(^block)();
    
    - (void)viewDidLoad {
    
        [super viewDidLoad];
         
    
    //    self.name = @"123";    
    
        int a = 3;
        
    
        void(^block)() = ^{
            
    
            NSLog(@"%d",a);
      
    
        };
    
        _block =  block;
    
        NSLog(@"%@",_block);
    
    }
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
    
    {
    
        _block();
    
    }
    

    思考?

    • 1.为什么block在堆里,也不能用weak来修饰?
      • 答:ARC下,默认局部变量对象都是强指针,block指针强引用block对象,代码块结束就会释放,所以需要对block对象再进行一次强引用。为什么要对局部变量进行一次强引用,就是为了防止代码块销毁
    • 2.为什么用strong不用copy
    • 答: @property (nonatomic, strong) NSString *name;
      现在这个视频讲的是对字符串不必用copy,因为不可变给不可变赋值,不用分配内存
    - (void)setName:(NSString *)name
    
    {
    
        _name = [name copy];
    
    }
    

    这个代码是copy要进行的操作,会在内部判断是否需要分布新的内存,如可变给不可变赋值需要分布内存,若不可变给不可变赋值就没必要分布内存

    - (void)setName:(NSString *)name
    
    {
    
        _name = name;
    
    }
    

    这个代码是strong要进行的操作,直接赋值
    总结:
    只要block没有引用外部局部变量,block放在全局区

          void(^block)() = ^{       
    
            NSLog(@"调用block%d");
    
        };   
    
        static int a = 3;(这个不是局部变量,这个static更改了a的生命周期)
    
        void(^block)() = ^{   
    
            NSLog(@"调用block%d",a);
    
        };
    
        //此时block没有引用外部的局部变量,在全局区里
    

    Block循环引用

    @property (nonatomic, strong) void(^block)();
    
    - (void)viewDidLoad {
    
        [super viewDidLoad];
    
        // block造成循环利用:Block会对里面所有强指针变量都强引用一次
    
        _block = ^{
    
            NSLog(@"%@",self);     
    
        };
    
        _block();
    
    }
    

    循环引用图:

    825e1998-3c3c-492f-a407-df7139380bce.png

    解析图:
    图:self里有block属性,只想block变量,而block变量里有self,会对里面的强指针变量强引用一次,self就会强指向该对象,就会造成相互引用。
    解决方法:

    @property (nonatomic, strong) void(^block)();
    
    - (void)viewDidLoad {    
    
        [super viewDidLoad];
    
        // block造成循环利用:Block会对里面所有强指针变量(所有的外部的对象变量)都强引用一次
    
        __weak typeof(self) weakSelf = self;
    
        _block = ^{
    
           NSLog(@"%@",weakSelf);       
    
        };
    
        _block();
    
    }
    

    指针图:


    e4ab9a31-6744-4090-93b4-67708e03f140.png

    指针图解析:把self包装成弱指针,block就不会再强引用了

    深入block循环利用:

    • 问题:若block里有延时操作,可能就已经销毁,需要再强引用来防止对象的销毁
    @property (nonatomic, strong) void(^block)();
    
    - (void)dealloc
    
    {
    
        NSLog(@"ModalViewController销毁");
    
    }
    
    - (void)viewDidLoad {
    
        [super viewDidLoad];
    
        // block造成循环利用:Block会对里面所有强指针变量都强引用一次
        
    
        __weak typeof(self) weakSelf = self;
        
    
        _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();
    
    }
    

    指针图:

    ba8ff633-71a7-4396-95b3-666a14e12ba3.png

    解析图:
    strongSelf指针对本对象强引用,__strong typeof(weakSelf) strongSelf 相当于局部变量,after block中就会用strongSelf指针对本对象强引用,最外层block执行完毕后,strongSelf指针会销毁、2s后after block会销毁之后本对象才会被销毁

    Block变量传递

    • 如果是局部变量,Block是值传递(外面修改不影响里面)
    - (void)viewDidLoad {
    
        [super viewDidLoad];
    
        int a = 3;      
    
        void(^block)() = ^{  
    
            NSLog(@"%d",a); //a为3       
    
        };
    
        a = 5; 
    
        block(); 
    
    }
    
    • 如果是静态变量,全局变量,__block修饰的变量,block都是指针传递(外面的修改影响block里面的值)
    - (void)viewDidLoad {
    
        [super viewDidLoad];    
    
        
    
        __block int a = 3;    
    
        void(^block)() = ^{ 
    
            NSLog(@"%d",a);    //值为5
    
        };
    
        a = 5;
    
        block(); 
    
    }
    

    Block开发使用场景(参数使用)

    • 把block当做参数,并不是马上就调用Block,什么时候调用,由方法内部决定
    • 什么时候需要把block当做参数去使用:做的事情由外界(非本类)决定,但是什么时候做由内部决定.
      需求:封装一个计算器,提供一个计算方法,怎么计算由外界决定,什么时候计算由内部决定.
    - (void)viewDidLoad {
    
        [super viewDidLoad];
    
        
    
        // 创建计算器管理者
    
        CacultorManager *mgr = [[CacultorManager alloc] init];
    
        [mgr cacultor:^(NSInteger result){
    
            result += 5;
    
            result += 6;
    
            result *= 2;
    
            return result;
    
        }]; 
    
        NSLog(@"%ld",mgr.result);
    
    }
    
    @implementation CacultorManager
    
    - (void)cacultor:(NSInteger (^)(NSInteger))cacultorBlock {
    
        if (cacultorBlock) {
    
          _result =  cacultorBlock(_result);
    
        }
    
    }
    

    block开发中使用场景(返回值)

    • 体现了一种思想:
    • 链式编程思想:把所有的语句用.号连接起来,好处:可读性非常好

    如: messay里的make.center.equalTo(ws.view);
    解析:make.center.equalTo得到一个block,make.center.equalTo(ws.view)去调用block

    • self.test();
      • 解析:self.test();发两步:1、self.test是调用get方法,返回一个block,2、self.test()调用block
     - (void(^)())test
    {
    //    void(^block)() = ^{
    //        
    //    };
        return ^{
            NSLog(@"调用了block");
        };
    }
    

    需求:封装一个计算器,提供一个加号方法

    
    - (void)viewDidLoad {
    
        [super viewDidLoad];
    
        CalculatorManager *mgr = [[CalculatorManager alloc] init];
    
        // mgr.add(5).add(5).add(5).add(5).add(5).add(5)
    
        // mgr.add(5)解析:mgr.add调用get方法返回一个block,mgr.add(5)调用的block是参数为5,mgr.add(5).add(5)需要返回mgr对象才可以一直.add调用下去
    
        mgr.add(5).add(5).add(5).add(5);
    
     }
    
    @implementation CalculatorManager
    
    - (CalculatorManager *(^)(int))add
    
    {
    
        return ^(int value){
    
            _result += value;
            
    
            return self;
    
        };
    
    }
    

    相关文章

      网友评论

          本文标题:Block相关

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