Nil

作者: 呆萌院长 | 来源:发表于2016-08-04 15:12 被阅读0次

    翻译自:http://ridiculousfish.com/blog/posts/nil.html

    发送一条消息给<code>nil</code>时候,Obejctive-C是如何处理的?它会返回什么值?你应该知道当一个方法定义了需要返回对象时候,将会获取到nil,但是如果方法定义返回<code>char</code>、<code>int</code>、<code>long long</code>、<code>float</code>、<code>struct</code>类型时候,它是如何处理的?

    接下来研究下。

    objc_msgSend

    这个函数是我们知道的。当一个对象发送一条消息时,例如<code>[someObject doSomething]</code>,编译器把这段代码转换成调用<code>objc_msgSend()</code>的函数(或者当方法声明返回<code>struct</code>变量时是调用<code>objc_msgSend_stret()</code>,下面会有详细介绍)。<code>objc_msgSend()</code>是如何处理nil的?

    幸运的是这段代码是开源的。我们需要的是objc-msg-ppc.s文件,.s后缀是组装的意思,不用担心这点。

    (接下来会在这篇博客中展示那个代码的代码片段。所有代码的版权都是苹果公司的)

    <code>objc_msgSend()</code>函数在那边?搜索<code> ENTRY _objc_msgSend</code>就可以找到。

    <pre>
    ENTRY _objc_msgSend
    ; check whether receiver is nil
    cmplwi r3,0 ; receiver nil?
    beq- LMsgSendNilSelf ; if so, call handler or return nil
    </pre>

    首先分析<code>cmplwi r3,0</code>这行,它是将<code>r3</code>(它是对象)和<code>0</code>作比较。接下来分析<code> beq- LMsgSendNilSelf </code>,它是如果<code>r3</code>和<code>0</code>相等时跳转到<code>LMsgSendNilSelf</code>。

    LMsgSendNilSelf

    <code>LMsgSendNilSelf</code>的实际是什么样子?在上面文件内搜索它就可以找到。它是消息接收者是nil的处理,但是它是如何处理的?

    <pre>
    LMsgSendNilSelf:
    mflr r0 ; load new receiver
    bcl 20,31,1f ; 31 is cr7[so]
    1: mflr r11
    addis r11,r11,ha16(__objc_nilReceiver-1b)
    lwz r11,lo16(__objc_nilReceiver-1b)(r11)
    mtlr r0
    cmplwi r11,0 ; return nil if no new receiver
    beqlr
    mr r3,r11 ; send to new receiver
    b LMsgSendReceiverOk
    </pre>

    <code>LMsgSendNilSelf</code>前六行是加载全局变量<code>__objc_nilReceiver</code>到寄存器<code>r11</code>中(六行仅仅是加载变量。位置独立代码的全局变量不是那么漂亮的)。然后它通过<code>cmplwi r11, 0</code>把变量和<code>nil</code>对比,如果它是<code>nil</code>,则<code>objc_msgSend()</code>返回。如果没有返回值,它会跳转到返回处。

    (如果<code>__objc_nilReceiver</code>不为<code>nil</code>,它将会将<code>__objc_nilReceiver</code>移到寄存器<code>r3</code>中,在<code>r3</code>消息继续,意味着<code>__objc_nilReceiver</code>是替换默认消息为<code>nil</code>行为的地方。这样很酷吧!)

    What gets returned

    总结下,如果发送一条消息给<code>nil</code>,当执行C函数时,它不会返回任何一条指定的返回值。所以什么返回值会返回给我们了?下面假设Mac OS X在PowerPC上怎么返回值。

    它的声明函数依赖函数类型!这个东西全被写在《PowerPC Runtime Architecture Guide》,同时在你电脑<code>/Developer</code>某处有同样的文章。真是一团糟。接下来是《Function Return》。下面摘录了部分描述:

    <pre>
    1,如果函数返回了一个指针或者整型(而不是long long类型),返回值将跳转到第四通用寄存器r3中。
    2,如果函数返回浮点指针类型,返回值将进入一个浮点指针寄存器FPR1。
    3,如果函数返回long long类型,上半部分进入寄存器r3,下半部进入寄存器r4。
    4,struct返回工作原理如下:被调用者为返回值创建区域,并将一个指针粘贴到寄存器r3的对应区域(因此开始将参数放到寄存器r4而不是寄存器r3),被调用者负责拷贝返回值到那块区域。现在我们可以知道为什么struct返回值需要特殊的objc_msgSend_stret()函数了:所有的参数是一个它们常在的寄存器。
    </pre>

    但是稍等,寄存器<code>r3</code>不是我们存放对象发送消息的第一位置?事实如此!当你在声明需要返回对象的函数中发送消息给<code>nil</code>,你可以得到同样为<code>nil</code>的返回值。函数没有接触到一个对象参数,自返回值和<code>objc_msgSend()</code>收到参数的地址是一致的,如果接收者为<code>nil</code>,然后会返回值。

    Types, types, types

    <code>int</code>、<code>short</code>、<code>char</code>、<code>long</code>类型的返回值也会跳转到寄存器<code>r3</code>,和上面一样的。发送消息给<code>nil</code>会返回给你<code>0</code>。

    但是<code>long long</code>类型,它的返回值一半在寄存器<code>r3</code>,一半在寄存器<code>r4</code>?但是<code>objc_msgSend()</code>没有接触到寄存器<code>r4</code>,在这个例子中我们发送<code>selector</code>到寄存器<code>r4</code>中。因此,对于<code>long long</code>类型的情况,我们希望得到的前32位的全是<code>0</code>和等于<coed>selector</code>发送的底部32位。如果我们发送消息给<code>nil</code>,并期望是<code>long long</code>类型时,我们将永远不会得到返回值为<code>0</code>。相反,我们得到可转换为<code>long long</code>类型的<code>selector</code>!

    浮点指针?<code>objc_msgsend()</code>接触不到浮点寄存器,所以当发送消息时,我们需要会回到任何碰巧在寄存器FPR1中。

    好的,现在最棘手的案例是:<code>struct</code>。<code>objc_msgsend_stret()</code>和<code>objc_msgsend()</code>在处理<code>nil-receiver</code>的方式上一样的。记得被调用者要负责复制的返回值为调用者提供空间。因为<code>objc_msgsend_stret()</code>忽略这样做,由调用者提供的空间数据是不变的。这通常意味着类似<code>myPoint = [nil someMethodReturningPoint];</code>这种代码将导致<code>myPoint</code>(但这种行为是依赖于编译器的优化,所以你不应该依赖于它)。

    唷,我的帽子和你一起走过这篇博客。希望你会有新的发现。

    本文转载请注明原作者呆萌院长,如果你对这篇文章有更好的见解可以通过微信联系我。利益相关:本篇文章所有涉及到的软件均为笔者日常所用工具,无任何广告费用。

    相关文章

      网友评论

          本文标题:Nil

          本文链接:https://www.haomeiwen.com/subject/nuecsttx.html