首页 > 代码库 > iOS-重回block小白之路

iOS-重回block小白之路

在我刚刚接触iOS开发的时候,是通过MJ老师讲的OC基础入门的,iOS圈的人应该基本都知道MJ大神吧,即便如此大神,讲解完block之后我依然感觉晕晕乎乎的,直到后来真正进公司做项目,依然感觉这是自己的一个弱项,后来通过不断接触,对它可能有了更多的了解,但是不一定够全面够深入,现在准备通过自己看过的几篇觉得还不错的文章,系统的来总结一下block的使用。不多废话,下面开始:

 

1、我在平时读他人文章的时候对block常见的描述是匿名函数,再多一些描述就是可以在方法内部使用,也可以在方法外部使用,还能做参数使用。下面看一下它的简单定义方式和使用

  1 int x = 8;  2 - (void)blockTest10 {  3   4     int (^myBlock)(int) = ^(int b){  5         x = 5;  6         return x + b;  7     };  8     int result = myBlock(3);  9     NSLog(@"%d", result); //8 10 } 11  12 - (void)blockTest9 { 13     static int a = 8; 14     int (^myBlock)(int) = ^(int b){ 15         a = 5; 16         return a + b; 17     }; 18     int result = myBlock(3); 19     NSLog(@"%d", result); //4 20 } 21  22  23 - (void)blockTest8 { 24     static int a = 8; 25     int (^myBlock)(int) = ^(int b){ 26         return a + b; 27     }; 28     a = 5; 29     int result = myBlock(3); 30     NSLog(@"%d", result); //8 31 } 32 - (void)blockTest7 { 33     __block int a = 5; //加上__block前缀,就会传址进去 34     int (^myBlock)(int) = ^(int b){ 35 //        a = 2; //编译不再报错 36         return a + b; 37     }; 38     a = 7; 39     int result = myBlock(3); 40     //上面 a = 2 注释的情况下打印出的是10,解注释的情况下打印出的是5 41     NSLog(@"%d", result); 42      43 } 44  45 - (void)blockTest6 { 46     NSMutableArray *mutableArray = [NSMutableArray arrayWithObjects:@"one", @"two", @"three", nil]; 47     int result = ^(int a){ 48         [mutableArray removeLastObject]; //这里是传址而不是传值,因此这行代码会移除成功 49         return a * a; 50     }(5); 51      52     NSLog(@"array :%@", mutableArray); 53     NSLog(@"%d",result); //25 54 } 55  56 - (void)blockTest5 { 57     //这段代码你会发现打印值依然未变,这是因为block对a的使用只是a的值的使用,而不是地址的引用,它在内部把a的值5作为常量来使用,因此在外部再改变a的值不会对block内部的a值造成任何影响,并且这时在block内部a是不能改变的,它在这里相当于一个常量。 58     int a = 5; 59     int (^myBlock)(int) = ^(int b){ 60 //        a = 2; //编译报错 61         return a + b; 62     }; 63     a = 7; 64     int result = myBlock(3); 65     //依然打印出8 66     NSLog(@"%d", result); 67      68 } 69  70 - (void)blockTest4 { 71     int a = 5; 72     int (^myBlock)(int) = ^(int b){ 73         return a + b; 74     }; 75  76     int result = myBlock(3); 77     //打印出8 78     NSLog(@"%d", result); 79  80 } 81  82  //square参数的类型是int(^)(int) 83 - (void)blockTest3:(int(^)(int))square{ 84     NSLog(@"%d",square(3)); 85 } 86  87  88 - (void)blockTest2 { 89     //声明一个名为square的block,并且返回值和参数都是int型 90     int (^square)(int); 91     //为square赋实体,类似函数内部的执行模块 92     square = ^(int a){ 93         return a * a ; 94     }; 95     //调用block,这里跟C函数调用一模一样 96     int result = square(5); 97     //这里打印值为25 98     NSLog(@"%d", result); 99 }100 - (void)blockTest1 {101     //直接使用一个block实体来进行计算102     //前面说过block某些方面跟函数非常相似,所以在这里103     //a相当于参数,return的值相当于返回值,5相当于传入的参数104     int result = ^(int a){105         return a * a;106     }(5);107     NSLog(@"%d",result); //打印值为25108     //当然,我们几乎不会这样去使用它109 }

以上是十种简单的使用情景,具体的说明在注释里,我想应该足够详细了,反正在这里了解一下基本格式和简单使用就好,平时不会直接这样使用,所以不能说不重要,但不属于精髓部分,这些应该是小白都懂的东西。下面是调用:

 1 - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { 2      3 //    [self blockTest1]; 4 //    [self blockTest2]; 5     //上面是block的一些简单定义和使用的方式,下面看一下block作为参数的简单使用 6 //    [self blockTest3:^int(int a) { //执行这行代码,会打印出9 7 //        return a * a; 8 //    }]; 9     //如果上面的示例不太明白的话,可以继续往下看,后面会介绍block做参数在实际开发工作中的具体使用场景。10     11     //下面看一下block对外部变量的使用12 //    [self blockTest4];13     //可以把4和5对比来看14 //    [self blockTest5];15     16     //对象引用,传址的情况17 //    [self blockTest6];18     19     //如果外部变量不像上面一样是一个对象指针,那该怎么处理呢?20 //    [self blockTest7];21     22     //静态变量全局只有一份,所以不管在哪里访问改变的都是a本身23 //    [self blockTest8];24     25 //    [self blockTest9];26     27     //全局变量也一样28     [self blockTest10];29 }

好了,简单的东西简单说,如果你感兴趣还可以去我的github把demo弄下来自己运行看看。这是地址:https://github.com/alan12138/Classification-of-knowledge-points/tree/master/block%20iOS/1-block%E7%9A%84%E7%AE%80%E5%8D%95%E4%BD%BF%E7%94%A8

2、好,继续往下进行,再深入一点,下面是较常用的方式了,比较重要,用block在类之间传递信息

  前面已经说过,block某些方面很像函数(但是blockOC对象),所以定义一个block,相当于定义了一个函数,它既可以定义在方法内部也可以定义在方法外部(定义在外部的时候你可以把它看做一个全局变量),而且调用规则和函数一样,调用的时候才会执行block内部的代码;

  个人感觉从这里开始才真正进入主题,因为像之前那种做法基本没人会那么用,在同一个控制器内定义和使用block,如果是这种需求那就没有必要用block了,你用C函数或OC方法岂不是更方便?block最大的用处本人认为是在不同的类之间传递消息,类似代理,但是使用起来比代理更加灵活简便,但是对掌握不熟练地人来说也很容易出错,这也会让很多人望而生畏,之前的我也是如此,哈哈。

         好了,不BB那么多了,看到这就直接去看另外两个demo吧,代理传值和block传值,这两个已经在我之前的博客控制器之间传值的方式总结中讲过了,但是我打算在把代理和block两个demo拷过来,这样可以方便对比一下,并针对block再多加几句话。

第一步:在需要对外传值的控制器声明一个block。

1 //一般都会用这种方式声明一个block类型(返回值类型为空,参数类型为字符串)2 typedef void (^TestBlock) (NSString *str);

第二步:声明block类型属性。

1 //一般使用copy策略,因为在ARC环境下已经不再有存储在栈中的block了,而是在堆中。声明一个TestBlock类型的变量2 @property (nonatomic, copy) TestBlock testBlock;

第三步:在你认为需要传值出去的时机调用block,把你想要传递出去的信息传递出去。

1    //block传值2     //自己确定需要传递信息的时机,这里是返回上一页的时候传值3     //用if判断一下是为了安全性,只有block确实存在的时候才会调用,否则会出问题4     if(self.testBlock) {5         //调用block成员变量6         self.testBlock(@"绿色");7     }

第四步:调用block的时候会执行block实体,这时候就把消息传过来了。

1    //这里是block回传的值2     //在这里实现block的实体,并接收调用者传递过来的参数,这就实现了控制器之间传递信息3     nextVc.testBlock = ^(NSString *str) {4         NSLog(@"%@",str);5     };

代理这里就不说咯,自己可以把demo搞下来对比一下,github地址:https://github.com/alan12138/Classification-of-knowledge-points/tree/master/block%20iOS/2-block%EF%BC%88%E5%86%8D%E6%B7%B1%E5%85%A5%E4%B8%80%E7%82%B9%EF%BC%89

3、下面进行到第三阶段了,这里着重分析一下block的实现原理,虽然之前我在面试题blog中也简单分析过,但是这里打算放一些更详细一点的,我就直接把唐巧大大的博客链接放到这里了,因为过于底层的东西平时开发是很少会接触到的,但是对于爱刨根问底的我们-程序猿来说,还是很有诱惑力的,所以有兴趣的童鞋可以去看看,没兴趣的就继续往下吧。我在这里只摘抄一些我认为大家都应该知道的一些东西。

1)、闭包

闭包是一个函数(或指向函数的指针),再加上该函数执行的外部的上下文变量(有时候也称作自由变量)。block 实际上就是 Objective-C 语言对于闭包的实现。

2)、在 Objective-C 语言中,一共有 3 种类型的 block:

  1. _NSConcreteGlobalBlock 全局的静态 block,不会访问任何外部变量。
  2. _NSConcreteStackBlock 保存在栈中的 block,当函数返回时会被销毁。
  3. _NSConcreteMallocBlock 保存在堆中的 block,当引用计数为 0 时会被销毁

3)、NSConcreteMallocBlock 类型的 block 通常不会在源码中直接出现,因为默认它是当一个 block 被 copy 的时候,才会将这个 block 复制到堆中,目标的 block 类型被修改为 _NSConcreteMallocBlock。

4)、在 ARC 开启的情况下,将只会有 NSConcreteGlobalBlock 和 NSConcreteMallocBlock 类型的 block。

原本的 NSConcreteStackBlock 的 block 会被 NSConcreteMallocBlock 类型的 block 替代。证明方式是以下代码在 XCode 中,会输出<__NSMallocBlock__: 0x100109960>。在苹果的 官方文档 中也提到,当把栈中的 block 返回时,不需要调用 copy 方法了。

引用文章地址:http://blog.devtang.com/2013/07/28/a-look-inside-blocks/

 

上面这四条都来自于唐巧的博客,但是后面我还打算补充一点更通俗一点的东西。

 

5)Block在MRC下的内存管理

block在mrc的情况下,默认是存储在栈中的,因此不需要程序员自己对它做内存管理,即使引用了外部的对象,也不会对该对象的引用计数产生任何影响。

但是,一旦对block进行了copy操作,它便会被移到堆中,这时候便需要对它做内存管理,包括释放以及对它引用对象的释放,因为如果它存在堆中的时候,那么被它引用的对象引用计数会+1,所以这个对象需要释放两次。

 1 void(^myBlock)() = ^{ 2     NSLog(@"------"); 3 }; 4 myBlock(); 5  6 Block_copy(myBlock); 7  8 // do something ... 9 10 Block_release(myBlock);

那么如何避免当block存在于堆,又对其他对象做了强引用,并且这个对象又对block产生了强引用的情况(比如block内部使用了self)。

 1 void(^myBlock)() = ^{ 2      NSLog(@"------%@",self.view); 3  }; 4  myBlock(); 5   6  Block_copy(myBlock); 7   8  // do something ... 9  10  Block_release(myBlock);

由于对block进行了copy操作,这时候self对block是强引用的,那么block内部又对self做了强引用,就会造成强引用循环,造成内存泄漏。解决办法便是:

 1 __block typeof(self) weakSelf = self; 2 void(^myBlock)() = ^{ 3       NSLog(@"------%@",weakSelf.view); 4   }; 5   myBlock(); 6    7   Block_copy(myBlock); 8    9   // do something ...10   11   Block_release(myBlock);

加入了第一行之后,block便不会再对self做强引用了。

 

6)Block在ARC下的内存管理

  在ARC默认情况下,Block的内存存储在堆中,ARC会自动进行内存管理,程序员只需要避免循环引用即可.

 

 1 __weak typeof(self) weakSelf = self; 2 void(^myBlock)() = ^{ 3       NSLog(@"------%@",weakSelf.view); 4   }; 5   myBlock(); 6    7   Block_copy(myBlock); 8    9   // do something ...10   11   Block_release(myBlock);

ARC环境下,只需要把__block换成__weak就可以了,还有个什么长长的前缀也可以,但是我忘记了,__weak够了。

为什么在ARC环境下__block不行了呢?因为__block在ARC中并不能禁止block对所引用的对象进行强引用,解决办法可以是在Block中将这个强引用对象置空,但是不推荐这么做。

但是在需要修改外部变量的时候,还是需要使用__block对变量进行修饰才能对变量进行修改的。

同时,在block内部定义的变量,会在作用域结束时自动释放,block对其并没有强引用关系,且在ARC中只需要避免循环引用即可,如果只是block单方面地对外部变量进行强引用,并不会造成内存泄漏。

 第三阶段就写这么多,即使写到这里我还是觉得远远没到自己感兴趣的那个程度,只掌握到这个程度的话在实际项目应用方面还是比较菜的,所以这里不多废话了,还有两个小demo,没事的话可以clone下来看一下,或许会有点帮助:https://github.com/alan12138/Classification-of-knowledge-points/tree/master/block%20iOS/3-%E7%A8%8D%E7%A8%8D%E6%80%BB%E7%BB%93%E4%B8%80%E4%B8%8B

 

4、实用篇,写一点实际应用场景。

1、之前提到过,也是最常见的,block做参数。

比如AFN中

1 [[AFHTTPSessionManager manager] POST:@"" parameters:params progress:^(NSProgress * _Nonnull uploadProgress) {2         } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {3     //do something4         } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {5 6         }];7     

Masonry中:(请忽略我见不得人的命名)

1 [self.nameLaebl makeConstraints:^(MASConstraintMaker *make) {2         make.top.equalTo(self.productBtn.top);3         make.left.equalTo(self.productBtn.right).offset(nameLabelLeftMargin);4         make.right.equalTo(self.right).offset(nameLabelRightMargin);5     }];

拿AFN来说吧,AFN对上面方法的实现是这样的:

 1 - (AFHTTPRequestOperation *)POST:(NSString *)URLString 2                       parameters:(id)parameters 3                          success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success 4                          failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure 5 { 6     AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithHTTPMethod:@"POST" URLString:URLString parameters:parameters success:success failure:failure]; 7  8     [self.operationQueue addOperation:operation]; 9 10     return operation;11 }

继续向下调用:

 1 - (void)setCompletionBlockWithSuccess:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success 2                               failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure 3 { 4  5         dispatch_async(http_request_operation_processing_queue(), ^{ 6             if (self.error) { 7                 if (failure) { 8                     dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{ 9                         failure(self, self.error);10                     });11                 }12             } else {13                 id responseObject = self.responseObject;14                 if (self.error) {15                     if (failure) {16                         dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{17                             failure(self, self.error);18                         });19                     }20                 } else {21                     if (success) {22                         dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{23                             success(self, responseObject);24                         });25                     }26 27     };28 }

这里负责把方法中block参数中的参数传递出去,而在我们使用这个方法的时候,便在block中接收它传递出来的结果,最常用的是responseObject,AFN返回的网络数据就存在这个值中。

而在我们使用POST方法的时候,就把success block内实现的代码块和failure block内的代码块层层传递到了AFN内部函数,然后等待网络请求的回应失败或者成功就调用相应的block,最后把获取的结果通过AFN方法中的block参数中的参数传给我们。

总结一下:先在block内部实现一个代码块,然后在合适的时候调用该block并传入参数,就可以实现对该代码块的调用,达到回调的目的。(不过AFN属于提供给别人用的,所以我理解的是这个顺序:你调用它的接口方法的时候,便实现了它block接口的实体,这样它在内部调用这个block的时候便有了实体,便成功调用参数block把block中的参数传递出来,所以block做参数的时候,一般block自己的参数便是传递信息的核心媒介)block就是一个对象,和OC中其他的对象一样,所以可以被当做参数来传递,区别是block是一个匿名函数,所以你可以调用它实现某些功能。

 

2、block做返回值

这里我写了个demo,直接看demo的代码吧:

先新建一个Person类:

 1 #import <Foundation/Foundation.h> 2  3 @interface Person : NSObject 4  5 - (NSString * (^)(NSUInteger))speak; 6  7 - (void (^)(NSUInteger))eat; 8 @end 9 10 11 @implementation Person12 - (NSString *(^)(NSUInteger))speak {13     return ^ NSString * (NSUInteger a) {14         return [NSString stringWithFormat:@"my age is %ld", a];15     };16 }17 18 - (void (^)(NSUInteger))eat {19     return ^(NSUInteger a) {20         NSLog(@"eat 了 %ld 个鸭梨", a);21     };22 }23 @end

定义两个公共方法供外部调用,返回值都是block类型。

接下来看一下怎么在控制器中使用:

 1 Person *p = [[Person alloc] init]; 2      3 //    NSLog(@"%@",[p speak](5)); 4 //     5 //    [p eat](6); 6      7     //其实上面这种做法是没有必要的,因为如果你真的想实现这种功能的话不需要多走一层block,但是这里是为了使用点语法实现链式调用,所以应该是下面这么用的 8     NSLog(@"%@",p.speak(5)); 9     10     p.eat(6);11     12     //是不是感觉有点像Masonry中的链式语法,点语法调用函数,就是调用该函数的的getter方法

OK,下面会讲到链式语法。上面代码的demo:https://github.com/alan12138/Classification-of-knowledge-points/tree/master/block%20iOS/4-%E5%AE%9E%E7%94%A8%E7%AF%87/block%E5%81%9A%E8%BF%94%E5%9B%9E%E5%80%BC

3、block链式语法

先看一个场景:

 1 - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { 2     NSUInteger a = 1,b = 2,c = 3,d = 4,e = 5; 3     NSUInteger result = 0; 4     result = [self add:a b:b]; 5     result = [self add:result b:c]; 6     result = [self add:result b:d]; 7     result = [self add:result b:e]; 8     NSLog(@"%ld",result); 9 }10 11 - (NSUInteger)add:(NSUInteger)a b:(NSUInteger)b {12     return a + b;13 }

假设定义一个简单的计算两个整数相加之和的方法,当我们需要计算多个数之和的时候,便需要不断的调用,但是链式语法便要简单的多:(这里的举例可能不太合适,因为谁都知道计算两数之和没必要单独写个方法,这里只是为了说明链式语法的优点)

1 NSUInteger result = [NSObject makeCalculate:^(CalculateManager *mgr) {2         mgr.add(a).add(b).add(c).add(d).add(e);3     }];

好了,下面就来看一下具体的实现过程是怎么样的:

先新建一个计算管理类:

 1 @interface ATCalcManager : NSObject 2 @property (nonatomic, assign) NSUInteger result; 3  4 - (ATCalcManager *(^)(NSUInteger s))add; 5  6  7 @implementation ATCalcManager 8 - (ATCalcManager *(^)(NSUInteger s))add { 9     return ^ATCalcManager *(NSUInteger x) {10         self.result += x;11         return self;12     };13 }14 @end

再新建一个NSObject的分类:

 1 @class ATCalcManager; 2 @interface NSObject (ATCalc) 3 + (NSUInteger)makeCalc:(void(^)(ATCalcManager *mgr))block; 4 @end 5  6  7  8 @implementation NSObject (ATCalc) 9 + (NSUInteger)makeCalc:(void(^)(ATCalcManager *mgr))block {10     ATCalcManager *mgr = [[ATCalcManager alloc] init];11     block(mgr); //计算12     return mgr.result;13 }

在控制器中使用:

1  NSUInteger a = 1,b = 2,c = 3,d = 4,e = 5;2  NSUInteger result = 0;3 4 result = [NSObject makeCalc:^(ATCalcManager *mgr) {5         mgr.add(a).add(b).add(c).add(d).add(e);6     }];7  NSLog(@"%ld",result); //15

 

总结:1、先看控制器中的这行调用

1 result = [NSObject makeCalc:^(ATCalcManager *mgr) {2         mgr.add(a).add(b).add(c).add(d).add(e);3     }];

这个方法的参数是一个block,我们在这里定义这个block的参数实体,也就是我们想实现的链式语法。

 

2、Command + 鼠标左键进去看这个函数的具体实现,该方法内部初始化一个ATCalcManager实例对象mgr,然后作为block的参数传入block,然后调用该block,回调了我们上一步实现的block实体。

3、接下来执行的链式调用代码mgr.add(a).add(b).add(c).add(d).add(e);,可以看到add方法返回的是一个block,该block的实现是累加传递进来的值然后赋值给属性result保存下来,然后返回值是self,也就是ATCalcManager实例对象。这样又可以实现点语法继续调用add方法,最后return mgr.result;返回计算结果。

最后,实现链式调用的一个关键点:就是每次调用add方法必须返回自身,然后才可以继续调用,如此一致循环下去,实现这一切都是block的功劳。

demo:https://github.com/alan12138/Classification-of-knowledge-points/tree/master/block%20iOS/4-%E5%AE%9E%E7%94%A8%E7%AF%87/block%E9%93%BE%E5%BC%8F%E8%AF%AD%E6%B3%95

 4、block实现函数式编程

先看个示例:

 1 [[[[mgr calculate:^NSUInteger(NSUInteger result) { 2         result += 1; 3         return result; 4     }] printResult:^(NSUInteger result) { 5         NSLog(@"第一次计算结果为:%ld",result); 6     }] calculate:^NSUInteger(NSUInteger result) { 7         result -= 2; 8         return result; 9     }] printResult:^(NSUInteger result) {10         NSLog(@"第二次计算结果为:%ld",result);11     }];

计算和打印循环套用,逻辑过程清晰的连在一起,而且不需要中间变量。

下面看如何实现:

新建一个ATCalcManager类

 1 @interface ATCalcManager : NSObject 2 @property (nonatomic, assign) NSUInteger result; 3 - (instancetype)calculate:(NSUInteger(^)(NSUInteger result))calculateBlock; 4 -(instancetype)printResult:(void(^)(NSUInteger result))printBlock; 5 @end 6  7  8 @implementation ATCalcManager 9 - (instancetype)calculate:(NSUInteger (^)(NSUInteger result))calculateBlock10 {11     _result =  calculateBlock(_result);12     return self;13 }14 15 -(instancetype)printResult:(void(^)(NSUInteger result))printBlock{16     printBlock(_result);17     return self;18 }19 @end

和链式编程一样,上面两个函数的关键点仍然在于每次都必须返回self,这样才可以继续嵌套调用其他函数。函数的内部实现是做一些内部处理,然后传入参数来调用block。

demo:https://github.com/alan12138/Classification-of-knowledge-points/tree/master/block%20iOS/4-%E5%AE%9E%E7%94%A8%E7%AF%87/block%E5%87%BD%E6%95%B0%E5%BC%8F%E7%BC%96%E7%A8%8B

 5、block保存代码块

首先说一下回调的概念,直接从大神那里拿过来了,简单易懂的例子:你到一个商店买东西,刚好你要的东西没有货,于是你在店员那里留下了你的电话,过了几天店里有货了,店员就打了你的电话,然后你接到电话后就到店里去取了货。在这个例子里,你的电话号码就叫回调函数,你把电话留给店员就叫登记回调函数,店里后来有货了叫做触发了回调关联的事件,店员给你打电话叫做调用回调函数,你到店里去取货叫做响应回调事件。

然后看一下具体场景中的应用,一个很常见的需求:

在tableview的每行cell上都有一个按钮,你需要在这个按钮被点击的时候处理这个动作,但是这个动作显然不适合在view中解决,这时就需要借助block回调来传递事件。

1、首先在cell视图中定义一个block属性

1 @property(copy, nonatomic) void (^callBack)(ATModel *);

2、在cell的实现文件中监听按钮点击

1 [btn addTarget:self action:@selector(btnClick:) forControlEvents:UIControlEventTouchUpInside];2 3 //...4 5 - (void)btnClick:(UIButton *)btn {6     if (self.callBack) {7         self.callBack(self.model);8     }9 }

3、在cellForRowAtIndexPath方法中为block赋实体

1 cell.callBack = ^(ATModel *model) {2         NSLog(@"%ld---%@",indexPath.row,model.name);3     };

这样你想要的点击rowIndex和对应的model都很容易的获取到了。

如果还不明白,直接运行一下demo,看一下输出就明白了。

 demo:https://github.com/alan12138/Classification-of-knowledge-points/tree/master/block%20iOS/4-%E5%AE%9E%E7%94%A8%E7%AF%87/block%E4%BF%9D%E5%AD%98%E4%BB%A3%E7%A0%81%E5%9D%97

 

总结:

做个总结吧,block有时候理解起来确实不那么顺,老感觉有点别扭有点绕,但是它也不过是个普通的OC对象而已,用习惯了就好了。最后推荐一个计算并缓存cellHeigth的小分类工具代码,很惭愧非原创,是我从大牛哪里学来的,做了一点微不足道的改动自己就用上了,哈哈,不过感觉里面的block使用的很值得看看,毕竟是实实在在在实际项目中应用的,而不是简单的demo简单介绍一下的就能比拟效果。代码在这里,有兴趣可以看看:https://github.com/alan12138/Tools/tree/master/rowHeightTest

iOS-重回block小白之路