代码块的本质是和其他的变量类似,不同的是,代码块存储的数据是一个函数体。使用代码块,你可以像调用其他标准函数一样的调用,可以传入参数,并得到返回值。
     脱字符是代码块的语法标记。下图表示代码块的定义。

1.代码块的基本使用

  1. //无参数无返回值
  2. void (^myblock)() = ^()
  3. {
  4. NSLog(@"Hello, World!");
  5. };
  6. myblock();
  7.  
  8. //带参数无返回值
  9. void (^myblock2)(NSString *string) = ^(NSString *string){ NSLog(@"%@",string);};
  10. myblock2(@"Hello, World myblock2!");
  11.  
  12. //无参数有返回值
  13. int (^myblocksss)() = ^(int i){return 12;};
  14. int c = myblocksss();
  15. NSLog(@"%i",c);
  16.  
  17. //有参数有返回值
  18. int (^myblock3)(int) = ^(int i){ return 12 * i; };
  19. int i = myblock3(3);
  20. NSLog(@"%i",i);

2,利用typedef为Block进行重命名

使用typedef为block进行一次重命名,方法跟为函数指针进行重命名是一样的:

  1. // Copyright © 2016年 liujun. All rights reserved.
  2. //
  3.  
  4. #import <Foundation/Foundation.h>
  5.  
  6. typedef int (^ MyBlock)(int a,int b);
  7.  
  8. int main(int argc, const char * argv[]) {
  9. @autoreleasepool {
  10. // insert code here...
  11.  
  12. __block int n = 100;
  13.  
  14. MyBlock block = ^(int a,int b)
  15. {
  16. n = 20; //不过没有用__block 修饰 代码不会编译通过
  17.  
  18. return n + a + b;
  19. };
  20.  
  21. NSLog(@"%i %i", n ,block(3,4)); //输出结果 100 27
  22.  
  23. NSLog(@"%i %i", block(3,4) ,n);//输出结果 27 20
  24. //以上输出。说明代码块是在调用的时候才会被执行
  25.  
  26. NSLog(@"Hello, World!");
  27. }
  28. return 0;
  29. }

  

3.Block在内存中的位置

根据Block在内存中的位置分为三种类型NSGlobalBlock,NSStackBlock, NSMallocBlock。

NSGlobalBlock:类似函数,位于text段;
NSStackBlock:位于栈内存,函数返回后Block将无效;
NSMallocBlock:位于堆内存。

  1. // Copyright © 2016年 liujun. All rights reserved.
  2. //
  3.  
  4. #import <Foundation/Foundation.h>
  5.  
  6. typedef long (^Sum)(int,int);
  7.  
  8. int main(int argc, const char * argv[]) {
  9. @autoreleasepool {
  10. // insert code here...v
  11.  
  12. Sum sum1 = ^ long (int a, int b) {
  13. return a + b ;
  14. };
  15. NSLog(@"sum1 = %@", sum1);// 打印结果:sum1 = <__NSGlobalBlock__: 0x47d0>
  16.  
  17. int base = 100;
  18. Sum sum2 = ^ long (int a, int b) {
  19. return base + a + b;
  20. };
  21. NSLog(@"sum2 = %@", sum2); // 打印结果:sum2 = <__NSMallocBlock__: 0xbfffddf8>
  22.  
  23. Sum sum3 = [sum2 copy];
  24. NSLog(@"sum3 = %@", sum3); // 打印结果:sum3 = <__NSMallocBlock__: 0x902fda0>
  25. NSLog(@"Hello, World!");
  26. }
  27. return 0;
  28. }

  NSGlobalBlock,我们只要实现一个没有对周围变量没有引用的Block,就会显示为是它。而如果其中加入了对定义环境变量的引用,就是NSStackBlock。那么NSMallocBlock又是哪来的呢?malloc一词其实大家都熟悉,就是在堆上分配动态内存时。没错,如果你对一个NSStackBlock对象使用了Block_copy()或者发送了copy消息,就会得到NSMallocBlock。这一段中的几项结论可从代码实验得出。

也就得到了下面对block的使用注意点。

对于Global的Block,我们无需多处理,不需retain和copy,因为即使你这样做了,似乎也不会有什么两样。对于Stack的Block,如果不做任何操作,就会向上面所说,随栈帧自生自灭。而如果想让它获得比stack frame更久,那就调用Block_copy(),让它搬家到堆内存上。而对于已经在堆上的block,也不要指望通过copy进行“真正的copy”,因为其引用到的变量仍然会是同一份,在这个意义上看,这里的copy和retain的作用已经非常类似。

4,外部参数在代码块的使用

blk1和blk2的区别在于:

blk1没有使用Block以外的任何外部变量,Block不需要建立局部变量值的快照,这使blk1与一般函数没有任何区别

blk2与blk1唯一不同是的使用了局部变量base,在定义(注意是“定义”,不是“运行”)blk2时,局部变量base当前值被copy到栈上,作为常量供Block使用。执行下面代码,结果是203,而不是204。

  1. int base = 100;
  2. base += 100;
  3. BlkSum sum = ^ long (int a, int b) {
  4. return base + a + b;
  5. };
  6. base++;
  7. printf("%ld",sum(1,2));

在Block内变量base是只读的,如果想在Block内改变base的值,在定义base时要用 __block修饰:__block int base = 100;

  1. __block int base = 100;
  2. base += 100;
  3. BlkSum sum = ^ long (int a, int b) {
  4. base += 10;
  5. return base + a + b;
  6. };
  7. base++;
  8. printf("%ld\n",sum(1,2));
  9. printf("%d\n",base);

输出将是214,211。Block中使用__block修饰的变量时,将取变量此刻运行时的值,而不是定义时的快照。这个例子中,执行sum(1,2)时,base将取base++之后的值,也就是201,再执行Blockbase+=10; base+a+b,运行结果是214。执行完Block时,base已经变成211了。

      static变量、全局变量 :如果把上个例子的base改成全局的、或static。Block就可以对他进行读写了。因为全局变量或静态变量在内存中的地址是固定的,Block在读取该变量值的时候是直接从其所在内存读出,获取到的是最新值,而不是在定义时copy的常量。

  1. static int base = 100;
  2. BlkSum sum = ^ long (int a, int b) {
  3. base++;
  4. return base + a + b;
  5. };
  6. base = 0;
  7. printf("%d\n", base);
  8. printf("%ld\n",sum(1,2)); // 这里输出是3,而不是103
  9. printf("%d\n", base);

  输出结果是0 4 1,表明Block外部对base的更新会影响Block中的base的取值,同样Block对base的更新也会影响Block外部的base值。

Block变量,被__block修饰的变量称作Block变量。 基本类型的Block变量等效于全局变量、或静态变量。

5,循环引用

retain cycle问题的根源在于Block和obj可能会互相强引用,互相retain对方,这样就导致了retain cycle,最后这个Block和obj就变成了孤岛,谁也释放不了谁。比如:

  1. @implementation TsetBlock
  2.  
  3. -(id)init{
  4.  
  5. if (self = [superinit]) {
  6. self.testStr =@"中国";
  7. self.block = ^(NSString *name, NSString *str){
  8. NSLog(@"arr:%@",self.testStr); // 编译警告:Capturing 'self' strongly in this block is likely to lead to a retain cycle
  9. };
  10. }
  11. returnself;
  12. }
  13. @end

  网上大部分帖子都表述为"block里面引用了self导致循环引用",但事实真的是如此吗?我表示怀疑,其实这种说法是不严谨的,不一定要显式地出现"self"字眼才会引起循环引用。我们改一下代码,不通过属性self.testStr去访问String变量,而是通过实例变量_testStr去访问,如下:

  1. @implementation TsetBlock
  2.  
  3. -(id)init{
  4.  
  5. if (self = [superinit]) {
  6. self.testStr =@"中国";
  7. self.block = ^(NSString *name,NSString *str){
  8. NSLog(@"arr:%@", _testStr); // 同样出现: Capturing 'self' strongly in this block is likely to lead to a retain cycle
  9. };
  10. }
  11. returnself;
  12. }
  13. @end

  可以发现:
即使在你的block代码中没有显式地出现"self",也会出现循环引用!只要你在block里用到了self所拥有的东西!

要分两种环境去解决:在ARC下不用__block ,而是用 __weak 为了避免出现循环引用

1.ARC:用__week

__weaktypeof (self)  weakSelf = self; 或者

__weak someClass *weakSelf = self;

2.MRC:用__block ,__block修饰的变量在Block copy时是不会retain的,所以,也可以做到破解循环引用。
__block someClass *blockSelf = self;

bloack的 retain、copy、release 操作

对Block不管是retain、copy、release都不会改变引用计数retainCount,retainCount始终是1;

NSGlobalBlock:retain、copy、release操作都无效;
NSStackBlock:retain、release操作无效,必须注意的是,NSStackBlock在函数返回后,Block内存将被回收。即使retain也没用。容易犯的错误是[[mutableAarry addObject:stackBlock],在函数出栈后,从mutableAarry中取到的stackBlock已经被回收,变成了野指针。正确的做法是先将stackBlock copy到堆上,然后加入数组:[mutableAarry addObject:[[stackBlock copy] autorelease]]。支持copy,copy之后生成新的NSMallocBlock类型对象。
NSMallocBlock支持retain、release,虽然retainCount始终是1,但内存管理器中仍然会增加、减少计数。copy之后不会生成新的对象,只是增加了一次引用,类似retain;
尽量不要对Block使用retain操作。

6.代码块的递归调用

代码块想要递归调用,代码块变量必须是全局变量或者是静态变量,这样在程序启动的时候代码块变量就初始化了,可以递归调用

  1. static void (^ const myblock4)(int) = ^(int i)
  2.  
  3. {
  4.  
  5. if(i > 0)
  6.  
  7. {
  8.  
  9. NSLog(@"%i",i);
  10.  
  11. myblock4(i - 1);
  12.  
  13. }
  14.  
  15. };

  

IOS Block代码块的定义与使用的更多相关文章

  1. iOS - Block 代码块

    1.Block Block 是一段预先准备好的代码,可以在需要的时候执行,可以当作参数传递.Block 可以作为函数参数或者函数的返回值,而其本身又可以带输入参数或返回值.Block 是 C 语言的, ...

  2. block(代码块)的介绍以及使用方法和变量之间的关系

    http://blog.csdn.net/menxu_work/article/details/8762848 block(代码块)的介绍以及使用方法和变量之间的关系 block(代码块)的介绍以及使 ...

  3. block代码块介绍

    关于block的简单介绍 什么是block? Block是C语言的一个语法特性,同时也是C语言的运行时特性,它很像C中的函数指针,因为你可以像使用函数指针一样的去使用block对象:它也很像C++中的 ...

  4. Block代码块中使用局部变量注意点

    第一次写代码遇到报这个错,实在是想不通为什么,按常理应该是不会有问题,报错的呀??纠结了一会之后只好仔细查看报错原因咯,原来是: 当我们在block代码块中使用局部变量时,就会很容易出现如图的错误. ...

  5. IOS学习4——block代码块

    本文转载自:iOS开发-由浅至深学习block 一.关于block 在iOS 4.0之后,block横空出世,它本身封装了一段代码并将这段代码当做变量,通过block()的方式进行回调.这不免让我们想 ...

  6. iOS - OC Block 代码块

    前言 Block 是一段预先准备好的代码,可以在需要的时候执行,可以当作参数传递.Block 可以作为函数参数或者函数的返回值,而其本身又可以带输入参数或返回值.Block 是 C 语言的,类似于一个 ...

  7. OC Block(代码块)

    #import "ViewController.h" @interface ViewController () @end @implementation ViewControlle ...

  8. ios 显示代码块(show the code snippet library)

    在项目的实际开发中,我们会重复的书写很多的代码,我经常是需要用到某一个功能,就从以前的项目中复制粘贴过来,很是麻烦 下面就为大家提供两种不错的方法, 一.宏定义,这个大家应该很熟悉,在这里就不做多的介 ...

  9. iOS:自定义代码块{ }

    1.Xcode本身带有编码常用的代码块可供使用,如下图 调用方法: (1)直接拖到代码区域中: (2)使用快捷键,键入 “while”, Xcode就会出现自动完成的提示 也可以自定义自己常用的代码块 ...

随机推荐

  1. js数字转换成财务金额

    function dealNumberToMoney(money){ var fmtAmt = ""; if(money&&money!=null){ money ...

  2. Mybaties 实现批量修改

    通常我们在做批量更新的时候都会用in 去操作,但in的数据量一上来,就变的缓慢了 修改方案: <update id="updateShufflingSeq" paramete ...

  3. JS设计模式(4)迭代器模式

    什么是迭代器模式? 定义:提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示. 主要解决:不同的方式来遍历整个整合对象. 何时使用:遍历一个聚合对象. 如何解决:把在元素之间游 ...

  4. linux --- 7. 路飞学城部署

    一.前端 vue 部署 1.下载项目的vue 代码(路飞学城为例), wget https://files.cnblogs.com/files/pyyu/07-luffy_project_01.zip ...

  5. 最短路模板|堆优化Dijkstra,SPFA,floyd

    Ⅰ:Dijkstra单源点最短路 1.1Dijkstra const int MAX_N = 10000; const int MAX_M = 100000; const int inf = 0x3f ...

  6. codeforce 955c --Sad powers 思路+二分查找

    这一题的题意是   定义一个数,该数特点是为a的p次方 (a>0,p>1) 再给你n个询问,每个询问给出一个区间,求区间内该数的数目. 由于给出的询问数极大(10e5) 所以,容易想到应该 ...

  7. ABAP search help (搜索帮助) 几种种方法

    ABAP search help (搜索帮助) 几种种方法    域范围  ABAP 的搜索帮助有很多种方法,掌握下面的几种基本差不多了 *&------------------------- ...

  8. spring-cloud-config-server——Environment Repository(Git Backend)

    参考资料: https://cloud.spring.io/spring-cloud-static/spring-cloud-config/1.4.0.RELEASE/single/spring-cl ...

  9. js获取css样式封装

    封装 function getStyle(obj , attr){ return obj.currentStyle?obj.currentStyle[attr]:getComputedStyle(ob ...

  10. 2018 German Collegiate Programming Contest (GCPC 18)

    2018 German Collegiate Programming Contest (GCPC 18) Attack on Alpha-Zet 建树,求lca 代码: #include <al ...