zoukankan      html  css  js  c++  java
  • 教你爱上Blocks(闭包)

    Blocks是C语言的扩充功能:带有自动变量(局部变量)的匿名函数。通过Blocks,源代码中就能使用匿名函数,即不带名称的函数。在我们 的工作中,命名占据了很大一部分,函数名,变量名,属性名,类名,框架名等都必须具备。能够编写不带名称的函数对程序员来说是具有相当吸引力的。

    Blocks 语法

    完整形式的Blocks 与一般的C语言函数相比较,有两点不同

    1. 没有函数名
    2. 带有 ^

    Blocks BN 范式

    Block_literal_expression ::= ^ block_decl compound_statement_body
    block_decl ::=
    block_decl ::= parameter_list
    block_decl ::= type_expression
    

    翻译成大白话就是

    ^ 返回值类型 (参数列表) 表达式

         1. 返回值类型 同 OC/C 语法中的返回值类型

         2. 参数列表 同 C 语法中的参数列表,OC中的参数是一个个传的,这里的语法更像 C 语言中以()包含的参数列表
         3. 表达式 同 OC/C 语法中允许使用的表达式

    ^int (int count){ return count++; }
    
    ^NSString * (NSNumber *num){
            return [NSString stringWithFormat:@"%@",num];
        };
    

    大家可能会疑惑,为什么平时看到的 Blocks 并不全是如此的,因为Blocks可以省略好几个项目

    Blocks 省略句式

    省略返回值类型

    如:

    ^ 返回值类型 (参数列表) 表达式

    可将返回值类型省略

    上面的代码例子可写为

    ^(int count){ return count++; }
    
    ^(NSNumber *num){
            return [NSString stringWithFormat:@"%@",num];
        };
    

    如此,看起来是不是熟悉多了。

    省略返回值类型时,如果表达式中有return语句就使用该返回值的类型,如果表达式中没有return语句就使用 void 类型,见下文代码例子。

    下面给出完整定义句式(使用省略返回值类型句式)

     ^(int count){return count+1 ;};   
    
     ^(NSNumber *num){
            return [NSString stringWithFormat:@"%@",num];
        };
    
     ^(NSNumber *count){
            NSLog(@"无返回类型Block count = %@", count);
        };  
     ^(void){
            NSLog(@"无返回类型也无参数 Block");
        };  
    

    省略参数列表

    如果不使用参数,参数列表也可省略

    ^ 返回值类型 (参数列表) 表达式

    如 上文无返回值类型,也无参数的Block也简化为

    ^{
         NSLog(@"无返回类型也无参数 Block");
    }; 
    

    Block 类型变量

    上面所讲述的Block从语法格式上来看与 除了无名称及带有 ^ 之外,与 C/OC 定义相同。

    在Block语法下, 可将 Block 语法赋值给声明为 Block 类型的变量中。 即源代码中一旦使用 Block 语法就相当于生成了可赋值给 Block 类型变量的 “值”。 Blocks 中由 Block语法生成的值也被称为 『Block』 。

    声明 Block 类型变量的示例如下:

    int (^counts)(int);
    

    Block 类型变量与其他 C/OC 变量没有任何区别,可以作为以下用途使用

    • 自动变量
    • 函数参数
    • 静态变量
    • 静态全局变量
    • 全局变量

    下面我们试着 用 Block 语法将 Block 赋值给 Block 类型变量

     int (^counts)(int) = ^(int count){return count+1 ;};   
    
     NSString *(^str)(NSNumber *num) = ^NSString *(NSNumber *num){
            return [NSString stringWithFormat:@"%@",num];
        };
    
    void (^blank)(NSNumber *count) = ^(NSNumber *count){
            NSLog(@"无返回类型Block count = %@", count);
        };  
    void (^blank)(void) = ^(void){
            NSLog(@"无返回类型也无参数 Block");
        };  
    

    在函数参数中使用 Block 类型变量 向函数 传递Block,即 将 Block 变量作为函数的 形参

    void func(int (^counts)(int)){
    }
    

    在 Block 作为类型变量传参时,记述方式及其复杂,如上文形参,这是我们可以像使用 函数指针类型时那样,使用 typedef 来简化记述方式。

     typedef int (^count) (int);
    

    重写上面的方法

    void func(count num){
    
    }
    

    这是 C 语言的写法,换成 OC 写法,让大家更加清楚一点,在定义时也是如此使用

    /**
     * 原来的写法
     */
    -(void)funcWithCount:(int (^)(int))count{
    
    }
    
    /**
     * 使用typedef之后的写法
     */
    -(void)funcWithCount:(count )count{
    
    }
    

    简单用法

    定义了 Block代码块 之后,就可以将一整块代码当做一个变量来使用,变量可为局部变量,也可为全局变量,这也是我认为 Block 最方便之处。

    假设要生成两个数组,一个装有5个随机数,一个装有10个随机数,将生成随机数的方法定义为一个闭包,在后文既可直接访问,如

      NSNumber *(^randArray)(void) = ^{
          int rand = arc4random() % 100;
    
          NSNumber *number = [NSNumber numberWithInt:rand];
          return number;
      };
    
      NSMutableArray *array1 = [[NSMutableArray alloc] init];
      NSMutableArray *array2 = [[NSMutableArray alloc] init];
    
      for (NSInteger index = 0; index<10; index++) {
          [array1 addObject:randArray()];
      }
    
      for (NSInteger index = 0; index<5; index++) {
          [array2 addObject:randArray()];
      }
    

    回调

    下面给出一 tableViewCell 上按钮事件回调的例子,这个也是很多人头痛的问题,通过block可以很方便地实现,而且层次非常清晰。

    自定义cell命名为blockCell,cell上放一 switch 控件,我们希望switch被点击时在viewController中可以得到switch的状态,获取到点击事件。

    blockCell.h 中定义一Block

    typedef void(^switchAction)(blockCell *);
    @property (nonatomic,copy)switchAction action;
    

    在switch的点击时间事件中调用switchAction

    blockCell.m

    - (IBAction)switchToggle:(id)sender {
        self.action(self);
    }
    

    viewController 中使用这个自定义Cell对table进行初始化

        -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    
            NSString *cellID = @"blockCell";
    
            blockCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
    
            cell.action = ^(blockCell *cell){
                // 既可取到行,也可取到switch状态
                NSLog(@"行数:%ld, switch state: %u", (long)indexPath.row, cell.switchBtn.on);
    
            };
    
            return cell;
        }
    

    传值

    现在很多流行地第三方库都将回调改成了Block,之前用的Delegate特别得心应手有木有,都封装好了直接调用得到我要的结果,好了,都改成Block,不知道如何去接Block的返回值,只能一遍又一般地重写。

    其实要封装很容易,将第三方库返回的Block,以一个Block来接住再返回调用的页面就可以了,本想介绍
    AFNetworing后再讲这个,但是我看了下,github上他们的主页的readMe写得超级清楚详细,想要了解的童鞋请仔细看下他们的readMe

    添加方式

    Github地址:https://github.com/AFNetworking/AFNetworking

    可以将类库拷贝到工程目录下添加,推荐用 cocoapods 安装,方便更新,而且不用手动导入framework,一键设置

    封装

    目的:将参数传递后调用对应的方法直接得到网络返回值

    新建一个类WebRequest ,此处写一个示例,大家自己参考

    #import <Foundation/Foundation.h>
    
    #import "AFNetworking.h"
    
    @interface WebRequest : NSObject
    
    
    -(void)requestNameWithID:(NSString *)ID 
                 WithSuccess:(void (^)(AFHTTPRequestOperation *operation, id responseObject, NSDictionary *myData))success
                     failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure;
    
    @end
    
    @implementation WebRequest
    
    -(void)requestNameWithID:(NSString *)ID 
                 WithSuccess:(void (^)(AFHTTPRequestOperation *operation, id responseObject, NSDictionary *myData))succes
                     failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
           {
    
    
        NSURL *url = [NSURL URLWithString:@"用ID拼接地接口地址"];
        NSURLRequest *request = [NSURLRequest requestWithURL:url];
        AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
        [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
    
            NSDictionary *dic = responseObject[@"someKey"];
    
            success(operation, responseObject, dic);// 此处将网络返回值传递给我们自己定义的Block中的三个返回值,dic可以自定义,也可不加,如此可以返回经自己在这里已经处理好的对象,而不必调用一次,处理一次
    
        } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
            failure(operation,error);// 与方法定义中的Block一致
        }];
    
    }
    
    
    @end
    

    调用

    WebRequest *request = [[WebRequest alloc] init];
    
    [request requestNameWithID:@"123" 
                   WithSuccess:^(AFHTTPRequestOperation *operation, id responseObject, NSDictionary *myData) {
    
        // 在网络成功时在这里就可以得到返回值了,operation,responseObject,myData
    
    
    }
                       failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    
        // 网络失败回调
    
    }];
    

    示例工程下载

  • 相关阅读:
    Educational Codeforces Round 85 D. Minimum Euler Cycle(模拟/数学/图)
    Educational Codeforces Round 85 C. Circle of Monsters(贪心)
    NOIP 2017 提高组 DAY1 T1小凯的疑惑(二元一次不定方程)
    Educational Codeforces Round 85 B. Middle Class(排序/贪心/水题)
    Educational Codeforces Round 85 A. Level Statistics(水题)
    IOS中的三大事件
    用Quartz 2D画小黄人
    strong、weak、copy、assign 在命名属性时候怎么用
    用代码生成UINavigationController 与UITabBarController相结合的简单QQ框架(部分)
    Attempting to badge the application icon but haven't received permission from the user to badge the application错误解决办法
  • 原文地址:https://www.cnblogs.com/Free-Thinker/p/4992630.html
Copyright © 2011-2022 走看看