Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/objective-c/26.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Objective c 编译器和运行时系统在生成的程序集中真正做了什么?_Objective C_Compiler Construction_Runtime - Fatal编程技术网

Objective c 编译器和运行时系统在生成的程序集中真正做了什么?

Objective c 编译器和运行时系统在生成的程序集中真正做了什么?,objective-c,compiler-construction,runtime,Objective C,Compiler Construction,Runtime,我想了解生成的程序集和运行时是如何协同工作的,并且在逐步完成一些生成的程序集代码时遇到了一个问题 源示例 下面是在XCode 4.5中运行的Objective-C的三行代码: // Line 1: NSObject *obj1 = [[NSObject alloc] init]; // Line 2: [obj1 release]; // Line 3: NSObject *obj2; 比较生成的程序集 在生成的程序集中,我进行了一些观察 在第1行之前,obj1的地址如下所示: obj1

我想了解生成的程序集和运行时是如何协同工作的,并且在逐步完成一些生成的程序集代码时遇到了一个问题

源示例

下面是在XCode 4.5中运行的Objective-C的三行代码:

// Line 1:
NSObject *obj1 = [[NSObject alloc] init];

// Line 2:
[obj1 release];

// Line 3:
NSObject *obj2;
比较生成的程序集

在生成的程序集中,我进行了一些观察

在第1行之前,obj1的地址如下所示:

obj1    (NSObject*) 0x00003604
在第1行之后,它将更改:

obj1    NSObject *  0x08122110
观察结果

1)
obj1
的地址已更改。编译源代码时,编译器会临时为
obj1
分配内存。然后,(在第1行之后)编译器显然会重新分配,因此对象的地址会改变

2) 第2行之后,
obj2
的地址仍然相同(
0x08122110
)!当我调用
[obj1 release]
时,我告诉编译器:“我不再需要这个了。请把它拿走。”但系统实际上是在将来某个时候发布的,我似乎无法直接控制它

3) 调试器无法跨过第3行。我不明白为什么它不会

问题

在创建和销毁对象方面,编译器实际如何处理这些代码行(特别是“alloc init”、一个发行版和一个没有赋值的NSObject指针声明)?还有,为什么调试器不让我跳过第三行?调试器不能看到它吗

除了回答,如果您能推荐一些关于编译器和运行时系统真正做什么的文档或书籍,我将不胜感激。多谢各位

  • 名为
    obj1
    的指针在堆栈上创建。它未初始化,这意味着它将包含该内存位置中的任何内容。由于使用未初始化的指针可能会导致未指定的行为,因此这是一个常见的错误源。一旦分配了对象,指针将用其地址初始化

  • 地址不会更改,因为指针未更新。当
    -release
    消息发送到对象时,保留计数器通常减少1。如果retain计数器已经在一个位置,则调用
    -dealloc
    方法,并将内存标记为空闲。只有指针指向的内存被标记为空闲,但指针保持不变。这就是为什么有些人喜欢在不再需要指针时将指针设置为
    nil

  • 您正在创建未初始化的指针。由于它没有初始化,它将重用已经存储在指针所在内存位置的数据


  • 关于这本书的推荐书。我会推荐编译器:原理、技术和工具。

    Marcus的回答很好,但这里有一些更多的细节(我一直想重温阅读生成的汇编;必须实际尝试并解释这是最好的方法)

    编译器编译对
    objc\u msgSend()
    的两个函数调用。第一个调用
    NSObject
    类上的
    +alloc
    方法。该函数调用的结果成为调用方法
    -init
    的第二个函数调用的第一个参数——目标对象

    然后,调用
    init
    的结果存储在堆栈中的一块内存中,该内存块已声明为名为
    obj1
    ,该内存块具有指向NSObject实例的指针类型


    您可以在调试器中单步执行这一行,因为这一行上有一个已执行的表达式。如果代码编写为:

    NSObject *obj1; // declaration
    obj1 = [[NSObject alloc] init];
    
    然后你会发现你不能单步通过声明

    obj1=[[NSObject alloc]init];之前;,在手动保留释放下,
    obj1
    的值为*未定义*,但在ARC**下**将自动设置为
    nil`(0)(从而消除了所示的错误源)

    此行调用由
    obj1
    指向的NSObject实例上的
    release
    方法

    NSObject *obj2; // Line 3
    
    这条线实际上什么也没做。如果编译器的优化器已打开,则根本不会生成任何代码。在没有优化器的情况下,编译器可能会通过
    sizeof(NSObject*)
    在堆栈上以
    obj2
    的名称保留空间来撞击堆栈指针

    同样,您不能在调试器中单步执行它,因为该行上没有要执行的表达式


    请注意,您可以将代码重写为:

    [[[NSObject alloc] init] release];
    
    就执行而言,这实际上与您编写的原始代码相同。如果没有优化器,它将有点不同,因为它不会在堆栈上存储任何内容。使用优化器,它可能会生成与原始代码相同的代码。优化器非常擅长在不需要局部变量时消除它们(这也是调试优化代码如此困难的部分原因)


    鉴于此:

    (11) void f()
    (12) {
    (13)    NSObject *obj1 = [[NSObject alloc] init]; // Line 1
    (14)    
    (15)    [obj1 release]; // Line 2
    (16)    
    (17)    NSObject *obj2; // Line 3
    (18)}
    
    这是未优化的x86_64程序集。忽略“修正”的东西。查看
    callq
    行;它们是对objc_msgSend()的实际调用,如上所述。在x86_64上,寄存器%rdi是所有函数调用的参数0。因此,%rdi是方法调用的目标所在rax是用于返回值的寄存器

    因此,当您看到一个callq,后面是
    movq%rax,%rdi
    ,后面是另一个callq,表示“获取第一个
    callq
    的返回值,并将其作为第一个参数传递给下一个
    callq

    至于变量,在
    callq
    之后会看到类似
    movq%rax,-8(%rbp)
    的内容。这表示“获取
    callq
    返回的内容,将其写入堆栈上的当前位置,然后将堆栈指针向下移动8
    [[[NSObject alloc] init] release];
    
    (11) void f()
    (12) {
    (13)    NSObject *obj1 = [[NSObject alloc] init]; // Line 1
    (14)    
    (15)    [obj1 release]; // Line 2
    (16)    
    (17)    NSObject *obj2; // Line 3
    (18)}
    
    _f:                                     ## @f
        .cfi_startproc
    Lfunc_begin0:
        .loc    1 12 0                  ## /tmp/asdfafsd/asdfafsd/main.m:12:0
    ## BB#0:
        pushq   %rbp
    Ltmp2:
        .cfi_def_cfa_offset 16
    Ltmp3:
        .cfi_offset %rbp, -16
        movq    %rsp, %rbp
    Ltmp4:
        .cfi_def_cfa_register %rbp
        subq    $32, %rsp
        leaq    l_objc_msgSend_fixup_release(%rip), %rax
        leaq    l_objc_msgSend_fixup_alloc(%rip), %rcx
        .loc    1 13 0 prologue_end     ## /tmp/asdfafsd/asdfafsd/main.m:13:0
    Ltmp5:
        movq    L_OBJC_CLASSLIST_REFERENCES_$_(%rip), %rdx
        movq    %rdx, %rdi
        movq    %rcx, %rsi
        movq    %rax, -24(%rbp)         ## 8-byte Spill
        callq   *l_objc_msgSend_fixup_alloc(%rip)
        movq    L_OBJC_SELECTOR_REFERENCES_(%rip), %rsi
        movq    %rax, %rdi
        callq   _objc_msgSend
        movq    %rax, -8(%rbp)
        .loc    1 15 0                  ## /tmp/asdfafsd/asdfafsd/main.m:15:0
        movq    -8(%rbp), %rax
        movq    %rax, %rdi
        movq    -24(%rbp), %rsi         ## 8-byte Reload
        callq   *l_objc_msgSend_fixup_release(%rip)
        .loc    1 18 0                  ## /tmp/asdfafsd/asdfafsd/main.m:18:0
        addq    $32, %rsp
        popq    %rbp
        ret
    Ltmp6:
    Lfunc_end0:
    
    Lfunc_begin0:
        .loc    1 12 0                  ## /tmp/asdfafsd/asdfafsd/main.m:12:0
    ## BB#0:
        pushq   %rbp
    Ltmp2:
        .cfi_def_cfa_offset 16
    Ltmp3:
        .cfi_offset %rbp, -16
        movq    %rsp, %rbp
    Ltmp4:
        .cfi_def_cfa_register %rbp
        .loc    1 13 0 prologue_end     ## /tmp/asdfafsd/asdfafsd/main.m:13:0
    Ltmp5:
        movq    L_OBJC_CLASSLIST_REFERENCES_$_(%rip), %rdi
        leaq    l_objc_msgSend_fixup_alloc(%rip), %rsi
        callq   *l_objc_msgSend_fixup_alloc(%rip)
        movq    L_OBJC_SELECTOR_REFERENCES_(%rip), %rsi
        movq    %rax, %rdi
        callq   *_objc_msgSend@GOTPCREL(%rip)
        .loc    1 15 0                  ## /tmp/asdfafsd/asdfafsd/main.m:15:0
        leaq    l_objc_msgSend_fixup_release(%rip), %rsi
        movq    l_objc_msgSend_fixup_release(%rip), %rcx
        movq    %rax, %rdi
        popq    %rbp
        jmpq    *%rcx  # TAILCALL
    Ltmp6:
    Lfunc_end0: