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); //打印值为25
108     //当然,我们几乎不会这样去使用它
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某些方面很像函数(但是block是OC对象),所以定义一个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 something
4         } 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 Person
12 - (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))calculateBlock
10 {
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

时间: 2024-11-14 15:45:49

iOS-重回block小白之路的相关文章

iOS中block类型大全

iOS中block类型大全 typedef的block 作为属性的block 作为变量的block 作为方法变量入参的block 作为方法参数的block 无名block 内联函数的block 递归调用的block 作为方法返回值的block 作为函数名的block(太过奇葩,完全不知道怎么用-_-!) iOS中block类型大全,码迷,mamicode.com

iOS中block的用法 以及和函数用法的区别

ios中block的用法和函数的用法大致相同 但是block的用法的灵活性更高: 不带参数的block: void ^(MyBlock)() = ^{}; 调用的时候  MyBlock(); 带参数的block: int ^(MyBlock)(int,int) = ^(int a,int b){return a+b;} 调用MyBlock(5,6); 将block当作某个类的属性的写法 typedef void (^BlockOption)(); @property (nonatomic,ass

IOS中Block的循环引用

@interface DemoObj() @property (nonatomic, strong) NSOperationQueue *queue; @end @implementation DemoObj - (instancetype)init { self = [super init]; if (self) { self.queue = [[NSOperationQueue alloc] init]; } return self; } - (void)dealloc { NSLog(@"

iOS 中Block以及Blocks的使用

一.ios中block的使用 Block可以帮助我们组织独立的代码段,并提高复用性和可读性.iOS4在UIKit中引入了该特征.超过100个的Apple API都使用了Block,所以这是一个我们必须开始熟悉的知识. Block是什么样的? 你可以使用^操作符来声明一个Block变量,它表示一个Block的开始. int num1 = 7; int(^aBlock)(int) = ^(int num2) { return num1+nunm2; }; 在如上代码中我们将Block声明为一个变量,

iOS中Block介绍(一)基础

ios开发block的使用指南,以及深入理解block的内存管理,也适用于osx开发.讨论范围:block的使用,内存管理,内部实现.不包含的内容:gc arc下的block内存,block在c++中的使用. AD: 一.概述 Block是C级别的语法和运行时特性.Block比较类似C函数,但是Block比之C函数,其灵活性体现在栈内存.堆内存的引用,我们甚至可以将一个Block作为参数传给其他的函数或者Block. 二.热身 先看一个比较简单的Block例子: int multiplier =

IOS开发---菜鸟学习之路--(二)-数据获取

http://www.cnblogs.com/PleaseInputEnglish/p/3432024.html IOS开发---菜鸟学习之路--(二)-数据获取,布布扣,bubuko.com

iOS中block用法之两个界面传值问题

Block的使用有很多方面,其中传值只是它的一小部分,但是很常用更实用,下面介绍Block在两个界面之间的传值用法: 先说一下思想: 首先,创建两个视图控制器,在第一个视图控制器中创建一个Label和一个Button,其中Label是为了显示第二个视图控制器传过来的字符串, Button是为了push到第二个界面. 第二个界面的只创建一个TextField,是为了输入文字,当输入文字并且返回第一个界面的时候(第二个视图将要消失的时候),就将这个 TextFiled中的文字传给第一个界面并且显示在

IOS中block的使用方法

X.1 初探Block 在这一小节我们先用一些简单范例来导入block的概念. X.1.1 宣告和使用Block 我们使用「^」运算子来宣告一个block变数,而且在block的定义最后面要加上「;」来表示一个完整的述句(也就是将整个block定义视为前面章节所介绍的简单述句,因为整个定义必须是一个完整的句子,所以必须在最后面加上分号),下面是一个block的范例: 1: int multiplier = 7 ; 2: int (^myBlock)( int ) = ^( int num) 3:

iOS之block浅谈

前言 ios4.0系统已开始支持block,在编程过程中,block被Obj-C看成是对象,它封装了一段代码,这段代码可以在任何时候执行.block可以作为函数参数或者函数的返回值,而其本身又可以带输入参数或返回值.它和传统的函数指针很类似,但是有区别:block是inline的,并且它对局部变量是只读的. block和函数的相似性:(1)可以保存代码(2)有返回值(3)有形参(4)调用方式一样. block的使用 1.block的定义 // 声明和实现写在一起,就像变量的声明实现 int a