iOS - OC Block 代码块

简介: 前言Block 是一段预先准备好的代码,可以在需要的时候执行,可以当作参数传递。Block 可以作为函数参数或者函数的返回值,而其本身又可以带输入参数或返回值。Block 是 C 语言的,类似于一个匿名函数,它和传统的函数指针很类似,但是 Block 是 inline(内联函数)的,并且默认情况下它对局部变量是只读的。

前言

  • Block 是一段预先准备好的代码,可以在需要的时候执行,可以当作参数传递。Block 可以作为函数参数或者函数的返回值,而其本身又可以带输入参数或返回值。Block 是 C 语言的,类似于一个匿名函数,它和传统的函数指针很类似,但是 Block 是 inline(内联函数)的,并且默认情况下它对局部变量是只读的。

  • 苹果官方建议尽量多用 Block。在多线程、异步任务、集合遍历、集合排序、动画转场用的很多。

  • Block 语法

        // Block as a local variable
        returnType (^blockName)(parameterTypes) = ^returnType(parameters) {...};
    
        // Block as a property
        @property (nonatomic, copy) returnType (^blockName)(parameterTypes);
    
        // Block as a method parameter
        - (void)someMethodThatTakesABlock:(returnType (^)(parameterTypes))blockName;
    
        // Block as an argument to a method call
        [someObject someMethodThatTakesABlock: ^returnType (parameters) {...}];
    
        // Block as typedef
        typedef returnType (^TypeName)(parameterTypes);
        TypeName blockName = ^returnType(parameters) {...};

1、Block 的使用

1.1 Block 的定义

  • Block 的简单定义

        // 定义 Block
        /*
            定义了一个名叫 MySum 的 Block 对象,它带有两个 int 型参数,返回 int 型。等式右边就是 Block 的具体实现,大括号后需加分号
        */
        int (^MySum)(int, int) = ^(int a, int b){
    
            return a + b;
        };
    
        // 调用 Block
        int sum = MySum(10, 12);
  • Block 数据类型的定义

        // 定义 block 数据类型 MyBlock
        typedef int (^MyBlock)(int, int);
    
        // 定义 MyBlock 的变量
        MyBlock myblock;
    
        // 实现 MyBlock 的变量 1
        myblock = ^(int a, int b){
    
            return a + b;
        };
    
        // 调用 MyBlock 的变量 1
        int sum = myblock(10, 12);
    
        // 实现 MyBlock 的变量 2
        myblock = ^(int a, int b){
    
            return a - b;
        };
    
        // 调用 MyBlock 的变量 2
        int minus = myblock(13, 2);

1.2 Block 访问局部变量

  • Block 可以访问局部变量,但是不能修改,如果要修改需加关键字 __block(双下划线)。

        // 这样定义时,局部变量 sum 只能读取值不能修改,编译时会报错
        // int sum = 10;
    
        // 这样定义时,局部变量 sum 既可以读取值又能修改
        __block int sum = 10;
    
        int (^MyBlock)(int) = ^(int a){
    
            // 对局部变量值修改
            sum ++;
    
            // 读取局部变量的值
            return a * sum;
        };
    
        int result = MyBlock(5);

2、Block 的回调

2.1 Block 回调使用

    // Block1.h
    
        // block 属性变量定义
        /*
            要使用 copy 类型,格式:@property (nonatomic, copy) 返回值类型 (^变量名) (参数类型列表);
        */
        @property (nonatomic, copy) void (^completion) (NSString *);
        
        // 调用 block 代码段声明
        - (void)useBlock;

    // Block1.m

        // 调用 block 代码段
        - (void)useBlock {
                
            // 设置 block 的回调值
                
            // 判断是否设置了 block
            if (self.completion != nil) {
            
                // 设置回调值
                self.completion(@"hello world");
            }
        }

    // Block.m
        
        #import "Block1.h"
        
        Block1 *block = [[Block1 alloc] init];
            
        // 设置 block 代码段
        block.completion = ^(NSString *str) {
            
            // 结果:string = @"hello world"
            NSString *string = str;
        };
            
        // 调用 block 代码段
        [block useBlock];

2.2 Block 回调封装

    // Block2.h
    
        // block 方法参数定义
        
        // 类方法定义
        + (Block2 *)blockWithCompletion:(void (^) (NSString *)) completion;
        
        // 调用 block 代码段声明
        - (void)useBlock;

    // Block2.m

        // block 属性变量定义
        
        // 要使用 copy 类型,格式:@property (nonatomic, copy) 返回值类型 (^变量名) (参数类型列表);
        @property (nonatomic, copy) void (^completion) (NSString *);
        
        // 调用 block 代码段
        - (void)useBlock {
                
            // 设置 block 的回调值
                
            // 判断是否设置了 block
            if (self.completion != nil) {
            
                // 设置回调值
                self.completion(@"hello world");
            }
        }
        
        // 类方法实现
        + (Block2 *)blockWithCompletion:(void (^)(NSString *))completion {
                
            Block2 *bl = [[Block2 alloc] init];
                
            // 设置属性的值
            bl.completion = completion;
                
            return bl;
        }

    // Block.m
        
        #import "Block2.h”
        
        // 设置 block 代码段
        Block2 *block = [Block2 blockWithCompletion:^(NSString *str) {
                
            // 结果:string = @"hello world"
            NSString *string = str;
        }];
            
        // 调用 block 代码段
        [block useBlock];

3、Block 属性定义中为什么使用 copy 修饰

  • ARC 开发的时候,编译器底层对 block 做过一些优化,使用 copy 修饰可以防止出现内存泄漏。

  • 从内存管理的角度而言,程序员需要管理的内存只有堆区的。如果用 strong 修饰,相当于强引用了一个栈区的变量。
  • 而使用 copy 修饰,在设置数值的时候,可以把局部变量从栈区复制到堆区。

        // 用 copy 修饰定义属性
        @property (nonatomic, copy) void (^myTask)();
    
        // 定义,myBlock 是保存在栈区的,出了作用域,就应该被销毁
        void (^myBlock)() = ^ {
    
            NSLog(@"hello");
        };
    
        // 用属性记录
        self.myTask = myBlock;
    
        // 执行
        self.myTask();

4、循环引用

  • 在 Block 中调用 self 容易产生循环引用,无法释放对象,在程序中可以使用析构方法判断是否产生了循环引用。

        @implementation ViewController
    
        // 在 Block 中调用 self 容易产生循环引用
        [[QWebImageManager sharedManager] downloadImage:self.urlStr completion:^(UIImage *image) {
            self.image = image;
        }];
    
        @end
        // 判断是否存在循环引用,无法释放时即存在循环引用
        - (void)dealloc {                                               
            NSLog(@"成功退出");
        }
  • 可以使用关键字 __weak 声明一个弱变量,或者为属性指定 weak 特性。如:

        @implementation ViewController
    
        // 弱引用 self,typeof(self) 等价于 ViewController
        __weak typeof(self) weakSelf = self;
    
        [[QWebImageManager sharedManager] downloadImage:self.urlStr completion:^(UIImage *image) {
            weakSelf.image = image;
        }];
    
        @end
目录
相关文章
|
7月前
|
Swift iOS开发
iOS OC混编Swift 后者无法走断点
iOS OC混编Swift 后者无法走断点
49 0
|
8月前
|
iOS开发
iOS block修饰符用copy还是strong
iOS block修饰符用copy还是strong
102 0
|
iOS开发 开发者
iOS开发 - 如何写出漂亮的block
iOS开发 - 如何写出漂亮的block
75 0
|
iOS开发
iOS开发- 关于Block的几种应用
iOS开发- 关于Block的几种应用
92 0
|
iOS开发 Python
iOS小技能:lldb打印block参数签名
iOS逆向时经常会遇到参数为block类型,本文介绍一个lldb script,可快速打印出Objective-C方法中block参数的类型。
159 0
iOS小技能:lldb打印block参数签名
|
自然语言处理 iOS开发
IOS——Block
IOS——Block
63 0
|
iOS开发
iOS代理 通知 block传值的规范写法
iOS代理 通知 block传值的规范写法
117 0
|
存储 Unix 编译器
|
存储 算法 iOS开发
|
Swift iOS开发 容器
iOS 仿支付宝银行卡界面(支持Swift/OC)
在有支付相关的APP中,都有对应的钱包,虽然现在的支付宝,微信支付很流行,但是都是需要绑定自己的银行卡,那么这个银行卡的卡包页面该怎么实现呢?在网上找了许久也没有找到合适的,那就索性自己造轮子。
324 0