草庐IT

iOS开发面试只需知道这些,技术基本通关!(block篇)

iOS是大鑫呀 2023-03-28 原文

一、什么是Block?

Block 是将函数及其执行上下文封装起来的对象。 比如:

通过 clang -rewrite-objc WYTest.m 命令编译该.m文件,发现该 block 被编译成这个形式:

其中 WYTest 是文件名,blockTest 是方法名,这些可以忽略。其中WYTest blockTest_block_impl_0 结构体为

--block_impl 结构体为

block内部有isa指针,所以说其本质也是 OC 对象

block 内部则为:

所以说 Block 是将函数及其执行上下文封装起来的对象

既然 block 内部封装了函数,那么它同样也有参数和返回值。

二、Block变量截获

1、局部变量截获 是值截获。 比如:

这里的输出是 6 而不是 2,原因就是对局部变量 num的截获是值截获。同样,在block里如果修改变量 num,也是无效的,甚至编译器会报错。

打印为 1,2,3

局部对象变量也是一样,截获的是值,而不是指针,在外部将其置为 nil,对block 没有影响,而该对象调用方法会影响

2、局部静态变量截获 是指针截获。

输出为 2,意味着 num = 1 这里的修改num值是有效的,即是指针截获。同样,在 block里去修改变量 m,也是有效的。

首先作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是本鑫一个iOS开发公众号:编程大鑫,不管你是小白还是大牛都欢迎入驻 ,让我们一起进步,共同发展!(群内会免费提供一些群主收藏的免费学习书籍资料以及整理好的几百道面试题和答案文档!)

##3、全局变量,静态全局变量截获:不截获,直接取值。 我们同样用clang 编译看下结果。

编译后

impl.isa= &_NSConcreteStackBlock;这里注意到这一句,即说明该block是栈 block

可以看到局部变量被编译成值形式,而静态变量被编成指针形式,全局变量并未截获。而--block修饰的变量也是以指针形式截获的,并且生成了一个新的结构体对象:

该对象有个属性:num5,即我们用--block 修饰的变量。这里--forwarding是指向自身的(栈 block)。

一般情况下,如果我们要对block 截获的局部变量进行赋值操作需添加--block 修饰符,而对全局变量,静态变量是不需要添加--block 修饰符的。

另外,block里访问 self 或成员变量都会去截获self

三、Block的几种形式

分为全局 Block(_NSConcreteGlobalBlock)、栈 Block(_NSConcreteStackBlock)、堆

Block(_NSConcreteMallocBlock)三种形式

其中栈 Block 存储在栈(stack)区,堆 Block 存储在堆(heap)区,全局Block 存储在已初始化数据(.data)

##1、不使用外部变量的block是全局block 比如:

输出:

2、使用外部变量并且未进行copy操作的block是栈block

比如:

输出:

日常开发常用于这种情况:

##3、对栈block进行copy操作,就是堆block,而对全局block 进行copy,仍是全局block

比如堆 1中的全局进行copy 操作,即赋值:

输出:

仍是全局block

而对 2中的栈block 进行赋值操作:

输出:

对栈 blockcopy之后,并不代表着栈 block就消失了,左边的 mallock是堆 block,右边被copy的仍是栈block 比如:

输出:

 即如果对栈Block进行cop,将会copy到堆区,对堆Block进行copy,将会增加引用计数,对全局 Block进行copy,因为是已经初始化的,所以什么也不做。

另外,--block变量在copy 时,由于--forwarding 的存在,栈上的--forwarding 指针会指向堆上的--forwarding变量,而堆上的--forwarding 指针指向其自身,所以,如果对--block 的修改,实际上是在修改堆上的--block变量。

--forwarding 指针存在的意义就是,无论在任何内存位置,都可以顺利地访问同一个 --block 变量

另外由于block 捕获的 --block 修饰的变量会去持有变量,那么如果用 --block修饰 self,且 self 持有

block,并且 block内部使用到--block修饰的 self时,就会造成多循环引用,即 self持有 blockblock  持有--block 变量,而--block变量持有self,造成内存泄漏。

比如:

如果要解决这种循环引用,可以主动断开--block变量对self的持有,即在 block内部使用完weakself后, 将其置为 nil,但这种方式有个问题,如果 block 一直不被调用,那么循环引用将一直存在。

所以,我们最好还是用--weak来修饰 self

以上就是block篇的面试题合集了,感谢观看~!

有关iOS开发面试只需知道这些,技术基本通关!(block篇)的更多相关文章

  1. ruby - 使用 C 扩展开发 ruby​​gem 时,如何使用 Rspec 在本地进行测试? - 2

    我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当

  2. ruby - RSpec - 使用测试替身作为 block 参数 - 2

    我有一些Ruby代码,如下所示:Something.createdo|x|x.foo=barend我想编写一个测试,它使用double代替block参数x,这样我就可以调用:x_double.should_receive(:foo).with("whatever").这可能吗? 最佳答案 specify'something'dox=doublex.should_receive(:foo=).with("whatever")Something.should_receive(:create).and_yield(x)#callthere

  3. ruby-on-rails - Enumerator.new 如何处理已通过的 block ? - 2

    我在理解Enumerator.new方法的工作原理时遇到了一些困难。假设文档中的示例:fib=Enumerator.newdo|y|a=b=1loopdoy[1,1,2,3,5,8,13,21,34,55]循环中断条件在哪里,它如何知道循环应该迭代多少次(因为它没有任何明确的中断条件并且看起来像无限循环)? 最佳答案 Enumerator使用Fibers在内部。您的示例等效于:require'fiber'fiber=Fiber.newdoa=b=1loopdoFiber.yieldaa,b=b,a+bendend10.times.m

  4. Ruby Sinatra 配置用于生产和开发 - 2

    我已经在Sinatra上创建了应用程序,它代表了一个简单的API。我想在生产和开发上进行部署。我想在部署时选择,是开发还是生产,一些方法的逻辑应该改变,这取决于部署类型。是否有任何想法,如何完成以及解决此问题的一些示例。例子:我有代码get'/api/test'doreturn"Itisdev"end但是在部署到生产环境之后我想在运行/api/test之后看到ItisPROD如何实现? 最佳答案 根据SinatraDocumentation:EnvironmentscanbesetthroughtheRACK_ENVenvironm

  5. ruby - 如何验证 IO.copy_stream 是否成功 - 2

    这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下

  6. ruby - 是否可以覆盖 gemfile 进行本地开发? - 2

    我们的git存储库中目前有一个Gemfile。但是,有一个gem我只在我的环境中本地使用(我的团队不使用它)。为了使用它,我必须将它添加到我们的Gemfile中,但每次我checkout到我们的master/dev主分支时,由于与跟踪的gemfile冲突,我必须删除它。我想要的是类似Gemfile.local的东西,它将继承从Gemfile导入的gems,但也允许在那里导入新的gems以供使用只有我的机器。此文件将在.gitignore中被忽略。这可能吗? 最佳答案 设置BUNDLE_GEMFILE环境变量:BUNDLE_GEMFI

  7. ruby - 在 Windows 机器上使用 Ruby 进行开发是否会适得其反? - 2

    这似乎非常适得其反,因为太多的gem会在window上破裂。我一直在处理很多mysql和ruby​​-mysqlgem问题(gem本身发生段错误,一个名为UnixSocket的类显然在Windows机器上不能正常工作,等等)。我只是在浪费时间吗?我应该转向不同的脚本语言吗? 最佳答案 我在Windows上使用Ruby的经验很少,但是当我开始使用Ruby时,我是在Windows上,我的总体印象是它不是Windows原生系统。因此,在主要使用Windows多年之后,开始使用Ruby促使我切换回原来的系统Unix,这次是Linux。Rub

  8. Ruby 文件 IO 定界符? - 2

    我正在尝试解析一个文本文件,该文件每行包含可变数量的单词和数字,如下所示:foo4.500bar3.001.33foobar如何读取由空格而不是换行符分隔的文件?有什么方法可以设置File("file.txt").foreach方法以使用空格而不是换行符作为分隔符? 最佳答案 接受的答案将slurp文件,这可能是大文本文件的问题。更好的解决方案是IO.foreach.它是惯用的,将按字符流式传输文件:File.foreach(filename,""){|string|putsstring}包含“thisisanexample”结果的

  9. ruby-on-rails - 在 Rails 开发环境中为 .ogv 文件设置 Mime 类型 - 2

    我正在玩HTML5视频并且在ERB中有以下片段:mp4视频从在我的开发环境中运行的服务器很好地流式传输到chrome。然而firefox显示带有海报图像的视频播放器,但带有一个大X。问题似乎是mongrel不确定ogv扩展的mime类型,并且只返回text/plain,如curl所示:$curl-Ihttp://0.0.0.0:3000/pr6.ogvHTTP/1.1200OKConnection:closeDate:Mon,19Apr201012:33:50GMTLast-Modified:Sun,18Apr201012:46:07GMTContent-Type:text/plain

  10. ruby - 在匿名 block 中产生 - 2

    我没有理解以下行为(另请参阅inthisSOthread):defdef_testputs'def_test.in'yieldifblock_given?puts'def_test.out'enddef_testdoputs'def_testok'endblock_test=procdo|&block|puts'block_test.in'block.callifblockputs'block_test.out'endblock_test.calldoputs'block_test'endproc_test=procdoputs'proc_test.in'yieldifblock_gi

随机推荐