Block总结

一、简介
Block代码块,本质上它和其它变量类似,不同的是代码块传递的是函数体,类似于@selector的功能。调用和其它标准函数一样。声明方式有差别。

二、代码块定义
例:int  ( ^    MyBlock)( int ) = ^  (int m){ return m * 3; };
        1    2         3         4       5        6          7
含义:
1:返回类型
2:^是代码块的语法标记
3:代码块的变量名
4:参数类型
5:定义代码块对象
6:参数名是m
7:代码块对象的主体部分

三、代码块调用
拿上面的为例:int newValue = MyBlock(3);

四、代码块语法的一些规则:
(1)当在block中直接使用局部变量时,局部变量会被当做是常量编码到block中(两个变量),所以不能在Block中直接修改局部变量
(2)代码块如果想要递归调用,代码块变量必须为全局变量或者静态变量。
(3)在代码块中可以使用和改变全局变量和静态变量。
(4)代码块可以使用局部变量,但是要改变值的话,要在局部变量前面加关键字__block。

五、Block存储域
根据Block中是否引用了外部变量,可以将Block存储区域分为三种:NSGlobalBlock、NSStackBlock、NSMallocBlock。
1.NSGlobalBlock-存储在全局数据区域:
对于没有引用外部变量的Block,无论在ARC还是非ARC下,类型都是__NSGlobalBlock__,这种类型的block可以理解成一种全局的block,和全局变量一样。同时,对他进行Copy或者Retain操作也是无效的。
2.NSStackBlock-存储在栈上:而对于引用了外部变量的block,如果没有对他进行copy,他的作用域只会在声明他的函数栈内(类型是__NSStackBlock__)。对其执行retain操作没有作用。
3.NSMallocBlock-存储在堆上:对NSStackBlock类型的block,执行copy操作,block会被复制到堆上。retain和copy对会使其引用计数加1。

说明:一般来说出问题的Block大部分都是NSStackBlock,超过了NSStackBlock的作用域NSStackBlock就会销毁。

六、什么时候要对NSConcreteStackBlock执行copy操作?
1.场景:配置在栈上的Block,如果其所属的变量作用域结束该Block就会废弃。这个时候如果继续使用该Block,就应该使用copy方法,将NSConcreteStackBlock拷贝为_NSConcreteMallocBlock。当_NSConcreteMallocBlock的引用计数变为0,该_NSConcreteMallocBlock就会被释放。
2.如果是非ARC环境,需要显式的执行copy或者antorelease方法。
3.而当ARC有效的时候,实际上大部分情况下编译器已经为我们做好了,自动的将Block从栈上复制到堆上。包括以下几个情况:
(1)Block作为返回值时
类似在非ARC的时候,对返回值Block执行[[returnedBlock copy] autorelease];
(2)方法的参数中传递Block时
(3)Cocoa框架中方法名中还有useringBlock等时
(4)GCD相关的一系列API传递Block时。
比如:[mutableAarry addObject:stackBlock];这段代码在非ARC环境下肯定有问题,而在ARC环境下方法参数中传递NSConcreteStackBlock会自动执行copy。

七、一般的应用场景:
假设A要调用B完成一件事,但是在B完成事情之后要通知A一下,这时候可以使用Block。
1.首先在B中定义一个Block类型,比如:
  typedef void (^DoSomeThingFinished)(id parame);
2.定义Block实例变量,DoSomeThingFinished aDoSomeThingFinished;
3.定义B的动作方法:-(void)doSomeThing:(DoSomeThingFinished)doSomeThingFinished;
4.动作方法实现规则:
(1)当doSomeThing方法被调用时,首先将doSomeThingFinished要copy一下(block将从栈赋值到堆上),并且赋值给aDoSomeThingFinished,以防止调用block时,block已经销毁。
(2)当事情完成时,首先检查代码块变量aDoSomeThingFinished是否为nil,如果不为nil,调用aDoSomeThingFinished代码块变量,并传入合适的值。然后release代码块变量aDoSomeThingFinished,并赋值nil。
5.在B销毁时,检查aDoSomeThingFinished是否为nil,如果不为nil,release,并且赋值nil。
6.A调用B的方法,如下:
[b doSomeThing:^(id parame){ /*动作完成时要做的事情*/ }];

八、Block循环引用:
(1)场景:假如A调用B,B的API使用了Block。在A中有一个B的实例作为成员变量,此时A引用了B;A在使用B的API的时候,在Block代码中使用了self关键字或者A的成员变量,导致block引用了A,即B引用了A。从而导致循环引用。
(2)导致的问题:如果Block被很好的执行,并且B release了Block,A的引用计数自然就降下来了,循环引用消失。但是,如果B长时间或者根本没有调用Block,导致B一直引用Block并且没有释放它,从而A的引用计数一直降不下来,导致A不能释放。
(3)解决办法:
MRC:重新定义一下self,如下:__block typeof(self) bself = self; 将self定义为__block类型,在block中使用bself变量,此时block就不会retain当前控制器了。当A销毁前,首先将B销毁掉,B销毁时,代码块被release并置nil,block将不会被执行。__block关键字告诉编译器,不要retain该变量。
ARC:对于ARC下, 为了防止循环引用, 我们使用__weak来修饰在Block中使用的对象。
(4)说明:在使用Block时,可以大胆的说,百分之九十九的情况下是不需要使用weakSelf的。
Block代码块引用self以至retain当前控制器,是有道理的,这样做是为了让代码块得到很好的执行,如果当前控制器已经释放了,在回调代码块的时候,就不正常了。
我们需要做的是,维护好block的调用关系以及生命周期就可以了,让block及时释放,对当前控制器的引用自然而然也就释放了。
那剩下的那百分之一是什么情况呢?即对Block的持有者一直保持Block不释放的情况,比如以Block的方式使用加速计,除非将加速计停止,否则Block一直会被持有,如果不希望这样,可以在Block中使用weakSelf。

九、Block内存演示代码:

//======================================================

//ARC:正确

//MRC:正确


void exampleA() {

    char a = 'A';

    ^{

        printf("%c\n", a);

    }();

}


//======================================================

//ARC:正确

//MRC:正确---EXC_BAD_ACCESS

//说明:添加的block在栈上,ARC下,block会被copyMRC下,如果没有执行copy操作,此block在函数体结束之后就释放了。

//addObject方法执行的是retain操作,不起作用。


void exampleB() {

    NSMutableArray *array = [NSMutableArray array];

    exampleB_addBlockToArray(array);

    void (^block)() = [array objectAtIndex:0];

    block();

}


void exampleB_addBlockToArray(NSMutableArray *array) {

    char b = 'B';

    [array addObject:^{

        printf("%c\n", b);

    }];

}


//======================================================

//ARC:正确

//MRC:正确

//说明:添加的Block为全局Block,函数体结束之后,它还存在。


void exampleC() {

    NSMutableArray *array = [NSMutableArray array];

    exampleC_addBlockToArray(array);

    void (^block)() = [array objectAtIndex:0];

    block();

}


void exampleC_addBlockToArray(NSMutableArray *array) {

    [array addObject:^{

        printf("C\n");

    }];

}


//======================================================

//ARC:正确

//MRC:正确,编译不通过--Returning block that lives on the local stack

//说明:添加的Block在栈上,ARC下,block会被copyMRC下,如果没有执行copy操作,此block在函数体结束之后就释放了。


typedef void (^dBlock)();


dBlock exampleD_getBlock() {

    char d = 'D';

    return ^{

        printf("%c\n", d);

    };

}


void exampleD() {

    exampleD_getBlock()();

}


//=======================================================

//ARC:正确

//MRC:不正确。这个例子和例子4类似,除了编译器没有认出有错误,所以代码会进行编译然后崩溃。更糟糕的是,这个例子比较特别,如果你关闭了优化,则可以正常运行。所以在测试的时候需要注意。


typedef void (^eBlock)();


eBlock exampleE_getBlock() {

    char e = 'E';

    void (^block)() = ^{

        printf("%c\n", e);

    };

    return block;

}


void exampleE() {

    eBlock block = exampleE_getBlock();

    block();

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/500146.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机PPT03,南京大学计算机网络课件03.ppt

第3讲数据链路层 3 1数据链路层的基本概念 一 链路与数据链路 链路 link 就是一条无源的点到点的物理线路段 中间没有任何其他的交换结点 数据链路 datalink 则是另一个概念 这是因为当需要在一条线路上传送数据时 除了必须有一条物理线路外 还必须有一些必要通信协议来控制这些…

NSThread使用总结

一、创建NSThread:(1)使用NSThread类方法:detachNewThreadSelector:toTarget:withObject:该方法3个参数分别指定了线程执行的方法,目标,和传递的对象。但是要注意,使用这个方法时,并没有返回任何对象供操作…

甘肃计算机教室中标,大单纷至沓来 甘肃百亿工程浪潮电脑中标8000台

近日,浪潮电脑教育市场再传佳讯,在甘肃百亿工程项目中,凭借出众的产品性价比和针对细分市场的定制化解决方案,浪潮电脑一举拿下了8000台的采购大单,这是继宁夏中标之后浪潮电脑在百亿工程中的又一成功典范,…

NSOperationQueue简介

一、NSOperationQueue简介1.概述:NSOperationQueue类管理NSOperation对象的执行。NSOperationQueue可以被称为操作队列,NSOperation可以被称为操作。操作被添加到操作队列之后,操作队列会根据操作对象的优先级或者相互之间的依赖关系来执行操…

计算机 窗口打开的方法,如何打开命令行窗口?两种实用的方法介绍

Windows系统中常常会用到命令行窗口,那么我们该如何打开命令行窗口?接下来,小编为大家介绍两种实用的打开命令行窗口的技巧,再也不必担忧如何打开命令行窗口的烦恼了。第一种打开方法1.点击自己的电脑中的开始菜单,在菜单中找到运…

GCD简介一:基本概念和Dispatch Queue

一、什么是GCD?Grand Central Dispatch或者GCD,是一套低层API,提供了一种新的方法来进行并发程序编写。从基本功能上讲,GCD有点像NSOperationQueue,他们都允许程序将任务切分为多个单一任务然后提交至工作队列来并发地…

大学生计算机二级大集报名最好,在校大学生如何报名计算机二级?

2009-12-05 回答首先要根据你的所学专业来衡量自己考哪项计算级二级有c语言, vb, vf, java, access, c 六项可选考其中一个你会的 考下来能拿计算机二级了考试分两部分,上机和笔试。笔试就是一些基本的操作…

GCD简介二:多核心的性能

一、概念为了在单一进程中充分发挥多核的优势,我们有必要使用多线程技术。在低层,GCD全局dispatch queue仅仅是工作线程池的抽象。这些队列中的Block一旦可用,就会被dispatch到工作线程中。提交至用户队列的Block最终也会通过全局队列进入相同…

怪哉翻译软件测试,[东方朔传翻译]东方朔传·怪哉原文与翻译

东方朔传怪哉原文与翻译原文:《太平广记》卷四七三引《东方朔传》汉武帝幸⑦甘泉,驰道中有虫,赤色,头牙齿耳鼻尽具,观者莫识①。帝乃使东方朔视之,还对②曰:“此虫名怪哉。昔时拘系⑧无辜&#…

GCD简介三:Dispatch Sources

一、何为Dispatch Sources简单来说,dispatch source是一个监视某些类型事件的对象。当这些事件发生时,它自动将一个block放入一个dispatch queue的执行例程中。我们到底讨论哪些事件类型?下面是GCD 10.6.0版本支持的事件:Mach por…

GCD简介四:挂起,目标指定,信号量

一、Dispatch Queue挂起dispatch queue可以被挂起和恢复。使用 dispatch_suspend函数来挂起,使用 dispatch_resume 函数来恢复。这两个函数的行为是如你所愿的。另外,这两个还是也可以用于dispatch source。一个要注意的地方是,dispatch que…

如何自己做网站发布到服务器上面,怎么做网站 四步教你搭建自己的网站

1、购买域名如果想要做一个自己的网站,购买一个自己喜欢的域名就是首先要做的!当你购买好自己的域名,就走出了搭建自己网站的第一步!2、购买空间/主机/服务器空间/主机/服务器,看着好像很复杂,其实很简单&a…

GCD简介五:补充

1.GCD在iOS4.0及以上可用。2.GCD中,主线程队列是串行的;全局队列是并行的,并由整个进程共享;用户自建队列,在iOS4.3以下,只能是串行,iOS4.3及以上,可以是并行的。3.dispatch_suspend…

ajax delete 传递参数,springMVC使用PUT、DELETE方法传递参数解决方案

在web.xml中新增过滤器HiddenHttpMethodFilterorg.springframework.web.filter.HiddenHttpMethodFilterHiddenHttpMethodFilter/*需要注意的是,只有context-type:application/x-www-form-urlencoded的请求才会被过滤。该过滤器的核心方法如下&#xff1a…

ARC简介

1、简介:ARC(Automatic Reference Counting),自动引用计数,需要XCode4.2和SDK5.0的支持。它是一个在编译期间工作的技术,编译器在编译的时候会根据变量的作用域为objective-c变量添加合适的retain、release、autorelease等函数。原…

微信验证码无法连接到服务器,微信无法连接到服务器1237解决方法

太多朋友在使用微信过程中出现微信无法连接到服务器1237的问题,出现这个问题的原因有很多种,接下来小编带大家找出问题原因,然后成功解决这个问题。以下是网友们成功解决此问题的方法,大家可以参考一下方法一:首先重启…

iOS后台任务

一、概览1.从iOS4.0开始,系统添加了多任务特征,允许应用在按下Home键之后,继续执行后台任务。大部分应用在进入后台之后就进入了挂起状态,只有那些为用户提供重要服务的App能够在进入后台之后继续运行一段时间。2.一般情况下&…

王者荣耀服务器维护bug,8月23日王者荣耀ios版更新一直显示维护是什么情况?更新出现bug 附处理方法...

相信很多喜爱玩王者荣耀的游戏玩家可以知道今天是王者荣耀星计划更新的日子,但是有很多苹果手机的小伙伴出现了一个问题,那就是无法进入游戏,甚至是更新十分的缓慢,在这里本文为您带来最新的解决方法!苹果手机微信区一…

服务器任务栏不显示程序,Win10任务栏不显示应用程序标签怎么办?

Win10任务栏不显示应用程序标签怎么办?执拗的人才会一条路走到黑,多个朋友多条路,多个方法多种选择。关于Win10任务栏不显示应用程序标签,一般是explorer进程出现故障导致,哦!忘记说了Win10系统下这东东改了个好听的名字&#xf…

Objective-C复制解析

一、为什么使用复制?1.C语言以及Objective-C语言总是按值传递的,按值传递意味着是隐式复制。在这里就分为两种情况:(1)如果是非对象类型,对传递过来的值进行更改,只会更改副本,对原始值没有影响&#xff0c…