Block(Closure) Tips

使用 Block 的时候谨记以下几点:

1.Block类型:全局块(Global Block)和堆块(Heap Block),以及栈块(Stack Block)。
2.变量捕获: 默认无法修改变量,需要添加 __block 修饰符
3.避免循环引用。

推荐文章:
1.官方文档:
快速上手:Working with Blocks,进阶:Blocks Programming Topics
2.优秀博客:
Deep into Block: A look inside blocks: Episode 1, Episode 2, Episode 3
底层实现:谈 Objective-C Block 的实现 - By 唐巧
类型探讨:Objective-C Blocks Quiz
啥都有:对 Objective-C 中 Block 的追探

类型

之前看文档或是其他人写的文章都讲述了三种 Block 类型,但直到看到上面的测试,我才意识 Block 类型是如何决定的。经过一些实践,简单来说,在 Objective-C 中:
1.Block 中没有使用外围变量的话,在开启 ARC 的条件下,因为不需要依赖其他状态,其使用的内存区域在编译期就可以确定,将会被编译为全局块;而没有开启 ARC 时,总是被编译为栈块。
在唐巧的博客中使用 Clang 来研究 Block ,这里也学习了一下,但是基本上这些块都是被编译为栈块,按照唐巧的说法,在 ARC 下,是被编译为全局块的。

《Effective Objective-C 2.0》给出如下的例子:声明了一个 Block,但需要根据条件来选择合适的实现。

void (^block)();
if(条件 A) {block = ^{NSlog(@"Block A");}
}else{ block = ^{ NSLog(@"Block B"); } } block()

上面这段代码的问题在于,这两个块只在相应的 if 或 else 语句范围内有效,离开了相应的范围后,编译器有可能覆写块所在的内存。这样的代码可以编译,但在运行时可能出错。书中提出,这里可以使用 copy 操作将块拷贝到堆上,这样一来,块可以在定义它的范围外使用。正确写法如下:

void (^block)();
if(条件 A) {block = [^{NSlog(@"Block A");} copy]; }else{ block = [^{ NSLog(@"Block B"); } copy]; } block()

书中没有提及此处是否开启了 ARC;我将上述代码编写在 C 文件中,使用 Clang 将之转化为 cpp 实现后,发现这里是个栈块;而按照上一条的说法,开启 ARC 后,这里将会被编译为全局块。那么在开启 ARC 的条件下,这段代码是否有问题呢?答案是没有,因为原来的代码的问题在于块的内存在栈中,而开启 ARC 下,块编译为全局块,不存在这个问题。
在 Objective-C Blocks Quiz 的 Example C中,提到:

That’s correct. Since the block doesn’t capture any variables in its closure, it doesn’t need any state set up at runtime. it gets compiled as an NSGlobalBlock. It’s neither on the stack nor the heap, but part of the code segment, like any C function. This works both with and without ARC.

2.使用了外围变量的话,若开启了 ARC,则只会被编译为堆块,内存是分配在堆上的;若没有开启 ARC,函数中定义的 Block 将会编译为栈块(如今除了旧项目很少有不开启 ARC 的吧)。

总结下:开启 ARC 的条件下,将不会有栈块,这样可以省去不少麻烦,但是 Objective-C Blocks Quiz 的最后也提到LLVM的一位维护者说:

We consider this to be a compiler bug, and it has been fixed for months in the open-source clang repository. What that means for any hypothetical future Xcode release, I cannot say. :)

保险点,开启 ARC。

生命周期

栈块,顾名思义,离开了定义它的函数范围就被收回了;堆块,就像普通的对象一样采用引用计数机制;全局块,在应用的整个生命周期都存在。

变量捕获 + 循环引用

在声明块的范围内,所有变量都可以被块捕获(就是可以使用)。默认情况下,不可以在块里修改外围的变量,因为块拷贝了一份变量到它的内存中,对于对象则是拷贝了对象的地址;若想修改,需要在外围变量前面添加修饰符 __block。是否添加 __block 修饰符,源代码会有很大不同,可以在唐巧的博客里看到。另外,Block 最著名的问题就是循环引用,就是由于互相保持着对方的引用,所以 ARC 拿这俩没办法,将会一直存在;复杂一点的,多个对象对其他对象的引用形成了一个圈,ARC 也是没办法。一般的解决办法是,在形成的引用圈的一处使用弱引用,这样就有机会打破强引用圈。

常见的循环引用陷阱是在类中定义了块变量,然后在块中使用了类实例的属性。而在访问属性的同时,块实质上隐式地捕获了当前实例,这样一来就造成了循环引用。
在 Objective-C 中,解决方法通常如下:

__weak myClass *weakSelf = self;
self.block = ^{__strong myClass *strongSelf = weakSelf;//防止当前 self 为空 if(strongSelf){ [strongSelf doSomeThing]; } }

又或者使用 typeof,不过这种高级技巧,可读性就不敢保证了。今天这条微博引起了一些讨论:


是否会捕获 self


这个代码的关键在于 typeof(self),因为它在块的内部出现,那么块是否捕获了 self呢?答案是不会,因为 typeof是个编译符号,在编译期间起作用,而不是运行时,因此这么写不会造成循环引用的问题。

在 Swift 中,针对这个问题有了更加优雅的解决方案:捕获列表(Capture List)。在闭包参数前添加列表,从属关系以及捕获对象成对为一组值,多组值用「,」隔开。
无参数:

var someClosure: () -> Void = {[unowned self, weak delegate = self.delegate!] in //closure body }

有参数:

var someClosure: (Int, String) -> String = {[unowned self, weak delegate = self.delegate!] (index: Int, stringToProcess: String) in //closure body }

 

转载于:https://www.cnblogs.com/rosee-1224/p/5167879.html

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

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

相关文章

【APICloud系列|33】通过程序循环数据集合的时候闭包加入imageCache方法

导读:一般实现的两种思路 1、通过程序循环数据集合的时候闭包加入imageCache方法。 2、通过递归数据集合实现 发现都不是我理想的效果,数据集合量较大的时候imageCache处理的时间比不用imageCache展示的时间要慢很多,展示会有明显延迟,最终采取以下方法 还是for循环将html拼…

Linux最危险的几个命令

Linux最危险的几个命令 Linux最危险的几个命令危险命令介绍删除文件和目录命令rmLinux 的 dd 命令mkfs 格式化硬盘分区shutdown> fileMore Linux最危险的几个命令 仅个人想法,会持续不间断更新和改进。 Linux系统中的命令最美妙也最危险。 如果几个操作系统&…

php维护session,维护带有cookie的PHP session_start()

我有一个PHP代码,可以使用session_start()启动会话.好了,在用户登录后,将被带到profile.php,其中显示了该用户信息.但是,当用户重新加载页面时,会话消失了.例如,有什么办法可以维持一个小时吗?我已经尝试过cookie,但是我不知道如何告诉PHP该会话已经开始.谢谢&#…

【APICloud系列|34】免费使用的ChromeDebug 模块

导读:很多人想要能够实现debug功能,即能够调试js,都说WeX5能够实现。。。 其实这种方式很简单的,在模块专区也有专门的模块提供,但是有人却抱怨收费,现在提供一个免费模块包,方便开发者使用。 使…

java asm tree_使用ASM 4处理Java类文件–第二部分:Tree API

java asm tree什么是ASM树API: ASM树API是ASM的一部分,可让您创建/修改内存中的类。 该类被视为信息树。 像整个类一样,它是ClassNode的实例,其中包含FieldNode对象的列表,MethodNode对象的列表等。本文假设读者已经在…

php 验证码文件,php实现的验证码文件类实例

本文实例讲述了php实现的验证码文件类。分享给大家供大家参考。具体如下:/*** file* version 1.0* author 网海浪子* brief 验证码文件类**/class ccheckcodefile{//验证码位数private $mcheckcodenum 4;//产生的验证码private $mcheckcode ;//验证码的图片privat…

【概率与期望】[UVA11021]Tribles

题目大意 k只麻球,每活一天就会死亡,但第二天可能会生一些麻球,具体是 生i个麻球的概率为pi ,求m天后所有麻球都死亡的概率。 LRJ such a dog,你给我个错的翻译。 分析 用f(i)表示一开始有1只麻球,i天后死亡的概率。 …

【APICloud系列|35】APICLloud开源官方模块

目前官方开源的模块有: 1,bMap(百度地图):https://github.com/apicloudcom/bMap 2,UIListView(可侧滑item的列表):https://github.com/apicloudcom/UIListView 3,aMap(高德地图):https://github.com/apicloudcom/aMap 4,UIPullRefresh(下拉刷新):https://gith…

Java无处不在:使用DukeScript在任何地方运行一次编写

在相当长一段时间内,Java都未能兑现“一次编写,随处运行”的承诺。 DukeScript希望通过在跨平台应用程序中实现视图和逻辑的清晰分离来改变这种状况。 在本文中,一个简单的场景用于介绍DukeScript的基础。 多年来,Java Swing使开…

数据仓库建设中的数据建模方法(转)

简介: 本文的主要内容不是介绍现有的比较流行的主要行业的一些数据模型,而是将笔者在数据仓库建设项目中的一些经验,在这里分享给大家。希望帮助大家在数据仓库项目建设中总结出一套能够合乎目前业界规范的,满足大部分行业数据仓库…

php实现标签云,php标签云的实现代码

数据库中,存放文章的表中有“Tag”字段,用来存放标签。标签之间用“,”分隔。比如“PHP,VB,随笔”。下面的实现代码,将标签从数据库中搜出来,并格式化处理,使其以出现的次数为依据显示出不同大小的文字连接。其中的细节…

【APICloud系列|36】 mobVerify免费短信验证码的实现

使用mobVerify之前,请到mob官网注册开发者账号,并申请api,大致流程如下: 1、网址:http://www.mob.com 2、注册登陆后鼠标放在右上角头像处即可看到"进入后台",点击进入 3、点击SecurityCodeSDK进入短信管理界面 4、点击顶部导航中的创建应用,填写信息即可获取…

PAT 1065 A+B and C (64bit) (20)

1065. AB and C (64bit) (20) 时间限制 100 ms内存限制 65536 kB代码长度限制 16000 B判题程序 Standard作者 HOU, QimingGiven three integers A, B and C in [-263, 263], you are supposed to tell whether AB > C. Input Specification: The first line of the input gi…

十大有用但又偏执的Java编程技术

经过一段时间的编码(以我为例,大约20年左右,当您玩得开心时光飞逝),人们开始接受这些习惯。 因为,你知道... 任何可能出错的事情都会发生。 这就是为什么人们会采用“防御性编程”的原因,即偏执…

php 接入微信 验证,PHP实现微信公众平台企业号验证接口

这篇文章主要介绍了PHP编程之微信公众平台企业号验证接口,是通过回调操作实现的企业号验证功能接口,需要的朋友可以参考下本文实例讲述了PHP微信公众平台企业号验证接口。分享给大家供大家参考,具体如下:微信公众平台企业号验证接口、回调 PHP版&#xf…

【APICloud系列|37】 银联支付的实现

正式使用请与银联签约端调用方法:xlUnionPayapi.require(xlUnionPay); xlUnionPay .xlUnionPay({params},callback(ret,err))params: spId :银联保留参数,默认为null(Android用) sysProvider :银联保留参数…

NGUI 3.5教程(二)Label 标签 (Hello world)、多行文本

写在前面: 本文将创建NGUI的第一个样例。依照编程传统,第一个样例,就是做一个Hello world 显示出来。NGUI。我们用Label来实现 。欢迎大家纠错、拍砖!原创非常辛苦,如有转载,请注明出处:htt…

【APICloud系列|38】 微信登录分享、QQ登录分享实现方法

对微信登录分享、QQ登录分享进行一个流程性的讲解。在微信分享经常是分享不成功或者图片不显示,主要问题是图片过大或者是没有本地化。 1、流程:使用auth进行授权--->getToken获取用户信息---->同步至服务端 注意:在安卓端可以提示用户没有安装微信端,但是ios端切…

PHP发送数据到指定方法,php通过header发送自定义数据方法_php技巧

下面小编就为大家分享一篇php通过header发送自定义数据方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧本文将介绍如何通过header发送自定义数据。发送请求时,除了可以使用$_GET/$_POST发送数据,也可以把数据…

JavaScript高级程序设计学习笔记第二十章--JSON

1.JSON:JavaScript Object Notation, JavaScript 对象表示法。 2.最重要的是要理解它是一种数据格式,不是一种编程语言。虽然具有相同的语法形式,但 JSON 并不从属于 JavaScript。而且,并不是只有 JavaScript 才使用 J…