Unh*_*lig 3 macos objective-c objective-c-runtime ios objective-c-blocks
这里的问题更多的是教育问题.我在一小时前开始想到这一点,同时翻转一个乐高积木(傻,我知道).
根据我的理解,块是在堆栈上创建的对象.
让我们说A是一个对象,这意味着我们可以做到:
[A message];
Run Code Online (Sandbox Code Playgroud)
基于此,如果块是一个对象,我们也可以这样做:
[block message];
Run Code Online (Sandbox Code Playgroud)
我对么?
当运行时看到它时,它会调用:
objc_msgSend(block, @selector(message), nil);
Run Code Online (Sandbox Code Playgroud)
所以我的问题是,我们如何发送一个消息块?
如果这是可能的,我会想象也可以发送一个块参数为块的消息?
并且,如果我们可以通过执行以下操作来调用块:
block();
Run Code Online (Sandbox Code Playgroud)
这是否意味着我们甚至可以将一个块作为message(SEL),因为块具有void (^)(void)类似于方法的签名?
因为如果有可能,那么下面的内容会让我感到惊讶:
objc_msgSend(block, @selector(block), block);
Run Code Online (Sandbox Code Playgroud)
要么:
objc_msgSend(block1, @selector(block2), block3);
Run Code Online (Sandbox Code Playgroud)
我希望我的想象力不会有点疯狂,我的理解不在这里(如果是的话,请纠正我).
bbu*_*bum 10
块是仅用于存储和引用的对象.通过使它们成为对象,可以保留/释放块,因此可以将块推入数组或其他集合类中.他们也回应copy.
就是这样.即使在堆栈上启动的块也主要是编译器实现细节.
调用块的代码时,不会通过objc_msgSend().如果您要阅读块运行时和llvm编译器的源代码,那么您会发现:
块实际上是一个C结构,它包含已捕获数据的描述(因此可以清除)和指向函数的指针 - 一块代码 - 这是块的可执行部分
块函数是标准C函数,其中第一个参数必须始终是对块的引用.参数列表的其余部分是任意的,就像任何旧的C函数或Objective-C方法一样.
所以,你的手动调用objc_msgSend()像任何其他随机ObjC对象一样处理块,因此,不会调用块中的代码,也不会调用它(如果有的话)(并且SPI可以通过方法执行此操作...但是,不要使用它可以传递一个完全可控的参数列表.
一方面,虽然相关.
imp_implementationWithBlock()获取一个块引用并返回一个IMP,该IMP可以插入到Objective-C类中(请参阅参考资料class_addMethod()),以便在调用该方法时调用该块.
imp_implementationWithBlock()的实现利用了objective-c方法与块的调用站点的布局.一块总是:
blockFunc(blockRef, ...)
Run Code Online (Sandbox Code Playgroud)
ObjC方法总是:
methodFunc(selfRef, SEL, ...)
Run Code Online (Sandbox Code Playgroud)
因为我们希望imp_implementationWithBlock()块始终将目标对象作为第一个块参数(即self)作为方法,imp_implementationWithBlock()返回一个trampoline函数,该函数在调用时(通过objc_msgSend()):
- slides the self reference into the slot for the selector (i.e. arg 0 -> arg 1)
- finds the implementing block puts the pointer to that block into arg 0
- JMPs to the block's implementation pointer
Run Code Online (Sandbox Code Playgroud)
该发现执行块位还挺有趣的,但无关紧要这个问题(地狱,在imp_implementationWithBlock()是有点不相干,太多,但可能有兴趣).
谢谢你的回复.这绝对是一个大开眼界.关于块调用的部分没有通过objc_msgSend()告诉我,这是因为块不是普通对象层次结构的一部分(但是coda提到的NSBlock似乎反驳了我到目前为止所理解的内容,因为NSBlock会使它成为对象层次).如果到目前为止我的理解还没有结束,请随意捅我.我非常有兴趣了解有关以下内容的更多信息:SPI以及调用该方法的方法(如何).2:基本机制:将自引用滑入插槽.3:找到实现块并将指向该块的指针放入arg 0.如果你有时间分享并详细阅读那些详细信息,我全都耳朵; 我发现这一切都非常吸引人.首先十分感谢.
这些块本身就是一个标准的Objective-C对象.块实例包含指向某些可执行代码的指针,任何捕获的状态,以及用于将所述状态从堆栈复制到堆(如果请求)并清除块破坏状态的一些帮助程序.
块的可执行代码不像方法那样被调用.A嵌段的其它方法- ,,retain 等... -可以直接被调用像任何其他方法,但可执行代码是不公开的那些方法之一.releasecopy
SPI没有做任何特别的事情; 它只适用于不带参数的块,它只不过是简单的做block().
如果你想知道整个参数幻灯片是如何工作的(以及它如何使尾部调用到块),我建议阅读这个或者这个.同样,块运行时,objc运行时和llvm的源都可用.
这包括IMP抓住块并将其推入arg0的有趣位.
| 归档时间: |
|
| 查看次数: |
471 次 |
| 最近记录: |