美文网首页16iOSIOS开发者学习笔记
04.实战技术 block深入研究,UICollectionVi

04.实战技术 block深入研究,UICollectionVi

作者: Liwx | 来源:发表于2016-01-16 01:41 被阅读301次

    @(iOS Study)[实战技术]


    目录

    • 04.实战技术 block深入研究,UICollectionView的使用
    • 1.block的深入研究
      • block基本使用
      • block开发使用场景(保存代码)
      • block开发使用场景(传值)
      • block内存管理(MRC)
      • block内存管理(ARC)
      • block循环引用
      • block循环引用(复杂)
      • block变量传递
      • block开发使用场景(参数使用)
      • block开发中使用场景(返回值)
    • 2.UICollectionView的使用
      • UICollectionView注意点


    1.block的深入研究

    block的作用: 可以用来保存一段代码段,也可以用来传递参数.block如果存放代码段,该代码段并不会马上执行,需用手动调用.

    • 快速生成block代码
      • 输入'inlineBlock'快速生成block代码
        -block类型是对象,不是普通数据类型

    block基本使用

    • block的格式
    // block的完整格式
    block返回类型(^block变量名)(block参数) = ^(block返回类型,一般情况都省略)(block参数>) {
        // block代码段
    }
    
    • block的声明方式
    // block声明:返回值(^Block变量名)(block参数类型),参数变量名可以省略
    void(^block)();
    void(^block1)(int);
    
    • block的定义方式
        // block定义: 等号右边 ^(参数类型 参数变量名){};
        void(^block2)(int a) = ^(int a){
            
        };
        
        // block定义二: 等号右边 ^返回值(参数类型 参数变量名){};,返回值(=号后面第一个int)可以省略,但是也有不省略
        int(^block3)(int a) = ^int(int a){
            return 2;
        };
        
        // block定义三: 当没有返回值,没有参数,可以省略
        void(^block4)() = ^{
            
        };
    
    

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

    • 模型中block类型属性的使用
      • 给模型添加一个block类型的成员属性,用来存放代码段.
      • 参考代码
    // 声明block类型的成员属性
    @interface CellItem : NSObject
    // 声明一个block类型的成员属性
    @property (nonatomic, strong) void(^block)();
    @property (nonatomic, strong) NSString *title;
    + (instancetype)itemWithTitle:(NSString *)title;
    @end
    
    // 给block添加代码段
    CellItem *item = [CellItem itemWithTitle:@"打电话"];
    item.block = ^{
        NSLog(@"打电话");
    };
    
    // 执行block,需判断block是否为空,如果为空,调用时会导致程序奔溃
    if (item.block) {
        item.block();
    }
    

    block开发使用场景(传值)

    顺传:定义属性
    逆传:代理,block,block可以用来替代代理

    • block逆传值步骤(控制器B由控制器A Modal出来的, 控制器B逆传值给控制器A)

      • 1.在控制器B添加一个block属性
      // 控制器B
      @property (nonatomic, strong) void(^blockValue)(NSString *value);
      
      • 2.在控制器A中给控制器B的block添加代码段
        // 控制器A
        // REMARKS: block传值(逆传)
        - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
        {
            ModalViewController *modalVc = [[ModalViewController alloc] init];
            // 给modalVc的block属性添加代码段
            modalVc.blockValue = ^(NSString *value){
                NSLog(@"%@", value);
            };
            [self presentViewController:modalVc animated:YES completion:nil];
        }
      
      • 3.在控制器B点击时调用block
        - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
        {
            // block逆传
            if (self.blockValue) {
                self.blockValue(@"123");
            }
            
            [self dismissViewControllerAnimated:YES completion:nil];
        }
      
      • 4.控制器B调用block时,会将@"123"传递给控制器A中的
      // 控制器B调用block会调用以下代码段,并将value传递过来
      modalVc.blockValue = ^(NSString *value){
          NSLog(@"%@", value);
      };
      

    block内存管理(MRC)

    • 内存5个区:堆,栈,方法区,常量区,全局区

      • 堆:手动管理内存
      • 栈:自动管理,代码块一过,就会自动释放.
    • 在MRC中的存储

      • 如果block没有访问外部的局部变量或者访问被static修饰局部变量,block默认存放在全局区.
      • 如果block访问外部的局部变量,block存放在"栈"里面
        // REMARKS: 测试block访问外部局部变量,block的存储
        - (void)test2 {
            
            // 1.验证block的存储区域
            int a = 10;
            void(^block)() = ^{
                NSLog(@"%d", a);
            };
            
            self.block = block;
            // ARC打印结果: <__NSMallocBlock__: 0x7fc433d0fab0> (block存储在堆区)
            // MRC打印结果: <__NSStackBlock__: 0x7fff51e24a18>  (block存储在栈区)
            NSLog(@"%@", block);
        }
      
      // REMARKS: 验证block的方法static修饰的局部变量,block的存储
      - (void)test1 {
          
          // 1.验证block的存储区域
          static int a = 10;
          void(^block)() = ^{
              NSLog(@"%d", a);
          };
          // ARC和MRC打印结果一样: <__NSGlobalBlock__: 0x10e1d9090>
          NSLog(@"%@", block);
      }
      
    • MRC使用block的注意点

      • MRC:不能使用retain声明block,block依然放在栈里面,会自动销毁.如果用retain声明的属性引用block,则程序奔溃.
      • MRC:使用copy声明block,才会放在堆里面
    • MRC开发习惯:访问属性或者设置属性,MRC必须使用点语法,不要使用下划线.因为点语法会调用get方法,get方法会做引用计数器+1操作.而用下划线没有对引用计数器+1.

    • MRC:没有strong,和weak, 只有assign, retain, copy

    • 区分MRC代码:

      • 1.看能否调用release retain retainCount
      • 2.ARC不能调用[super dealloc]

    block内存管理(ARC)

    • ARC环境block的存储区
      如果block访问外部的局部变量,block存放在"堆"里面

    • block原则

      • 如果block没有访问外部的局部变量或者局部变量被static修饰,block默认存放在"全局区"
      • ARC:使用strong声明block,不要使用weak.
    • ARC管理原则:默认一个局部变量对象,都是强指针,存放堆里面

    block循环引用

    block会对外部所有强指针对象给强引用,block不会对外部弱指针对象给强引用.

    • block代码段内部使用self

      • 正确用法: 使用__weak typeof(self) weakSelf = self;将self转换成弱指针.对象能正常被销毁
      // 使用__weak typeof(self) weakSelf = self;将self转换成弱指针.
      __weak typeof(self) weakSelf = self;
      _block = ^{
          NSLog(@"%@", weakSelf);
      };
      
      • 错误用法: 直接在block内部使用self,造成循环引用,对象不会被销毁.
      _block = ^{
          NSLog(@"%@", self);
      };
      

    block循环引用(复杂)

    • block在多线程中的应用

      • block内部执行延迟操作或者异步任务需要在block内部对weakSelf做一次__strong的引用,让对象延迟释放,以确保在执行延迟操作或异步任务时,对象还没被释放.
      • 以下block内部执行延迟操作,对象会延迟2秒后才释放.
        - (void)blockTest
        {
            // SINGLE: block内部执行延迟操作或者异步任务需要在block内部对weakSelf做一次__strong的引用
            
            __weak typeof(self) weakSelf = self;
            _block = ^{
                
                // 需要在block内部对weakSelf做一次__strong的引用,必须用__strong强引用weakSelf,这样才能延迟释放self对象,否则会出现延迟打印结果为null,在执行延迟任务前,对象已经被释放了.
                __strong typeof(weakSelf) strongSelf = weakSelf;
                
                // 执行延迟操作或异步任务,
                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                    NSLog(@"%@", strongSelf);
                });
                
            };
            
            // 执行block
            _block();
        }
      

    block变量传递

    • block访问外部局部变量没有被任何关键字修饰,都是值传递.值传递表示block中的value不会随外部的改变而改变.
    - (void)test
    {
        // block访问外部局部变量没有被任何关键字修饰,都是值传递.值传递表示外部的value值改变不会影响block内部的值.
        int value = 10;
        
        void(^block)() = ^{
            
            NSLog(@"%d", value);
        };
        
        value = 20;
        
        block();
        
        // 打印结果: 10
    }
    
    • block访问外部变量是全局变量或者被__block,static修饰,都是指针传递,block中的value会随外部的改变而变.
    - (void)test1
    {
        //  block访问外部变量是全局变量或者被__block,static修饰,都是指针传递,block中的value会随外部的改变而改变.
        static int value = 10;
        
        void(^block)() = ^{
            NSLog(@"%d", value);
        };
        
        value = 20;
        
        block();
        
        // 打印结果: 20
    }
    

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

    • block作为方法参数使用
    // block当参数使用 caculatorBlock带有一个参数result,并且返回值为int类型
    - (void)caculator:(int(^)(int result))caculatorBlock;
    

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

    • 链式编程思想:把方法调用通过点语法链接,可读性非常好 (Masonry框架使用)

    • 链式编程思想的简单使用,block当返回值

    - (void)test1
    {
        // self.add()相当于调用 add的get方法,get方法返回值是block类型的,再用返回值调用
    //    void(^block)() = self.add;
    //    block();
        
        self.add();
    }
    
    - (void(^)())add
    {
        return ^{
            NSLog(@"add");
        };
    }
    
    
    • 使用block当做返回值, 实现manager.add(10).add(5)链式编程

      • 1.先分析manager.minus(3)操作,相当于先调用get方法,返回block类型的值,再通过返回值调用block.
      // manager.minus(3)相当于执行以下两个操作
      // void(^block)(int) = manager.minus;
      // block(3);
      
      • 2.由以上可推出manager的方法声明
        - (void(^block)(int))minus;
        
        // 实现- (void(^block)(int))minus方法
        - (void(^block)(int))minus
        {
            return ^(int value){
                _result -= value;
            };
        }
      
      • 3.分析manager.minus(3).minus(10),如果想再调用.minus(10)执行操作,manager.minus(3)返回值必须是CalculatorManager类型对象,由此可以分析出,block的返回值不是void,而是CalculatorManager类型.
        - (CalculatorManager *(^)(int)) minus;
        
        // 实现- (CalculatorManager *(^)(int)) minus方法
        - (CalculatorManager *(^)(int)) minus
        {
            return ^(int value){
                _result -= value;
                // 下面的return表示block的返回值,返回当前对象
                return self;
            };
        }
      
      • 4.以上操作完成,外部就能用manager.minus(3).minus(10)链式编程.

    2.UICollectionView的使用

    UICollectionView注意点

    • UICollectionView注意点:
      • 1.初始化必须要传入布局,(流水布局:九宫格布局)
      • 2.UICollectionViewCell必须要注册
      • 3.必须自定义cell

    相关文章

      网友评论

        本文标题:04.实战技术 block深入研究,UICollectionVi

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