A look inside blocks: Episode 1
来源:互联网 发布:Ftp端口开放设置 编辑:程序博客网 时间:2024/05/01 05:48
原文地址:http://www.galloway.me.uk/2012/10/a-look-inside-blocks-episode-1/
一篇不错的文章地址:http://blog.csdn.net/wildfireli/article/details/22063001
Today I have been taking a look at the internals of how blocks work from a compiler perspective. By blocks, I mean the closure that Apple added to the C language and is now well and truly established as part of the language from a clang/LLVM perspective. I had been wondering just what a “block” was and how it magically seems to appear as an Objective-C object (you can copy
, retain
, release
them for instance). This blog post delves into blocks a little.
今天我从编译器的角度看看block的内部是如何工作的,
我是好奇一个block 是什么,并且it是怎么魔法般的作为一个oc对象出现(例如你能copy,retain,释放他们),这篇博客深入研究了block一点点。
The basics
This is a block:
This creates a variable called block
which has a simple block assigned to it. That’s easy. Done right? No. I wanted to understand what exactly the compiler does with that bit of code.
这里创建了一个叫block的变量,这个变量有一个简单的代码块(block)赋给它,那是简单的,确实嘛?不是,我想要确切的理解编译器是怎么处理那些代码的。
Further more, you can pass variables to block:
进一步,你能传递一个变量给block.
Or even return values from them:
或者甚至从代码块中返回值
And being a closure, they wrap up the context they are in:
作为一个闭包,他们总结了他们所在的上下文
So just how does the compiler sort all of these bits out then? That is what I was interested in.
所以编译器是怎么解决这些代码的呢,这正是我感兴趣的地方
Diving into a simple example
My first idea was to look at how the compiler compiles a very simple block. Consider the following code:
The reason for the two functions is that I wanted to see both how a block is “called” and how a block is set up. If both of these were in one function then the optimiser might be too clever and we wouldn’t see anything interesting. I had to make the runBlockA
function noinline
so that the optimiser didn’t just inline that function in doBlockA
reducing it to the same problem.
选这两个函数的原因是我想要看看一个block是怎么被呼唤的,同时想看看一个block 是怎么设置的。如果这两个函数都是在一个函数中,然后优化器也许太聪明,导致我们不能看到任何有趣的事情,我不得不使函数runBlockA 非内联的,以致优化器没有内联那个函数进doBlockA
The relevant bits of that code compiles down to this (armv7, O3
):
This is the runBlockA
function. So, that’s fairly simple then. Taking a look back up to the source for this, the function is just calling the block. r0
(register 0) is set to the first argument of the function in the ARM EABI.(ARM:Advanced RISC Machines,EABI:Embedded application binary interface 应用二进制接口) The first instruction therefore means that r1
is loaded from the value held in the adress stored in r0 + 12
. Think of this as a dereference of a pointer, reading 12 bytes into it. Then we branch to that address. Notice that r1
is used, which means that r0
is still the block itself. So it’s likely that the function this is calling takes the block as its first parameter.
这是一个runBlockA函数,那是相当的简单的,让我们来看一下这个函数的源头,
From this I can ascertain that the block is likely some sort of structure where the function the block should execute is stored 12 bytes into said structure. And when a block is passed around, a pointer to one of these structures is passed.
Now onto the doBlockA
method:
Well, that’s pretty simple also. This is a program counter relative load. You can just think of this as loading the address of the variable called ___block_literal_global
into r0
. Then the runBlockA
function is called. So given we know that the block object is being passed to runBlockA
, this ___block_literal_global
must be that block object.
Now we’re getting somewhere! But what exactly is ___block_literal_global
? Well, looking through the assembly we find this:
Ah ha! That looks very much like a struct to me. There’s 5 values in the struct, each of which are 4-bytes (long). This must be the block object that runBlockA
was acting upon. And look, 12 bytes into the struct is what looks suspiciously like a function pointer as it’s called ___doBlockA_block_invoke_0
. Remember that was what the runBlockA
function was jumping to.
But what is __NSConcreteGlobalBlock
? Well, we’ll come back to that. It’s ___doBlockA_block_invoke_0
and ___block_descriptor_tmp
that are of interest since these also appear in the assembly:
That ___doBlockA_block_invoke_0
looks suspiciously like the actual block implementation itself, since the block we used was an empty block. This function just returns straight away, exactly how we’d expect an empty function to be compiled.
Then comes ___block_descriptor_tmp
. This appears to be another struct, this time with 4 values in it. The second one is 20
which is how big the ___block_literal_global
is. Maybe that is a size value then? There’s also a C-string called .str
which has a value v4@?0
. This looks like some form of encoding of a type. That might be an encoding of the block type (i.e. it returns void and takes no parameters). The other values I have no idea about.
But the source is out there, isn’t it?
Yes, the source is out there! It’s part of the compiler-rt
project within LLVM. Trawling through the code I found the following definitions withinBlock_private.h:
Those look awfully familiar! The Block_layout
struct is what our ___block_literal_global
is and the Block_descriptor
struct is what our___block_descriptor_tmp
is. And look, I was right about the size being the 2nd value of the descriptor. The bit that’s slightly strange is the 3rd and 4th values of the Block_descriptor
. These look like they should be function pointers but in our compiled case they seemed to be 2 strings. I’ll ignore that little point for now.
The isa
of Block_layout
is interesting as that must be what _NSConcreteGlobalBlock
is and also must be how a block can emulate being an Objective-C object. If _NSConcreteGlobalBlock
is a Class
then the Objective-C message dispatch system will happily treat a block object as a normal object. This is similar to how toll-free bridging works. For more information on that side of things, have a read of Mike Ash’s excellent blog post about it.
Having pieced all that together, the compiler looks like it’s treating the code as something like this:
That’s good to know. It makes a lot more sense now what’s going on under the hood of blocks.
What’s next?
Next up I will take a look at a block that takes a parameter and a block that captures variables from the enclosing scope. These will surely make things a bit different! So, watch this space for more.
- A look inside blocks: Episode 1
- A look inside blocks: Episode 1
- A look inside blocks
- A Look Inside JBoss Cache
- A quick look inside the Android emulator
- Volatile fields in .NET: A look inside
- An Introduction to TrueType Fonts: A look inside the TTF format
- 2014年12月3日,A Look Inside Presentation Controllers
- node-haystack Episode 7: Asynchronously manipulate blocks
- A Look at Commons Chain, Part 1
- Take a Close Look at AdminUs - 1
- Michael Kors Tas The ultra-modern Now perspective inside Notice Facility offers an easy take a look
- A. Forgotten Episode(水题)
- CodeForces 440A Forgotten Episode
- have a look
- have a look!
- have a look
- is a detailed look
- Bzoj3513:[MUTC2013]idiots:FFT
- Android SurfaceView+MediaPlayer实现视频播放
- BeanUtils的使用
- Linux字符设备驱动实验
- Oracle-30-常用系统权限&查看当前用户权限&为用户授予创建视图的权利
- A look inside blocks: Episode 1
- algrothm_逆序输出,回文(找中点+换位置)
- mysqldump备份疑惑详解
- 求连乘最大
- XP下常用的或有用的小工具
- LeetCode 78, 90. Subsets i, ii
- object类的9个方法
- algrothm_逆序输出【堆栈】
- 使用Log4j.jar写日志到文件