15

我已阅读“摘要”部分中的过渡到 ARC 发行说明。他们告诉:

ARC 通过在编译时添加代码来确保对象在必要时存在,但不再存在。从概念上讲,它通过为您添加适当的内存管理调用来遵循与手动引用计数(在高级内存管理编程指南中描述)相同的内存管理约定。

为了让编译器生成正确的代码

我想知道 ARC 纠正了我们的代码的结果是什么。

我的问题:我们能看到变化吗?(在 alloc 、 retain 、assign 或 release 方面。不是汇编级别!)

原因:因为我觉得在没有ARC模式的老传统开发中能看到最佳实践的代码很好。

4

3 回答 3

27

clang 中的 ARC 不能通过将代码从 ObjC 重写为 ObjC 来工作,而是在代码生成期间发出额外的保留/释放 LLVM 位码。这意味着,如果不进入 LLVM IR / 汇编级别,就不可能知道编译器如何“修复”它。


如果 ARC 如您所说的那样发出 LLVM 位码。它是为了在编译过程中使用更少的时间而制作的吗?(不太复杂的 ObjC 代码,更少的头文件?)

如果编译器可以减少通过代码的次数总是更好的。


你能告诉我一些在汇编级别显示代码的示例或实用程序吗?

要获取汇编代码,您可以

  1. 直接从编译器生成程序集。在命令行中,-S调用编译器时添加标志。结果是一个.S包含汇编代码的文件。在 Xcode 项目中,打开源代码文件,然后转到Product(在菜单栏上)→ Generate OutputAssembly File

  2. 生成目标文件,然后反汇编它。内置命令otool -tvV <file>可以执行反汇编,并且有otx(免费)或IDA(免费评估)等高级工具。

我更喜欢路线 2,因为它产生的垃圾更少,并且可以配置反汇编工具以产生更多有用的信息。无论如何,无论使用哪种方法,您都需要能够阅读汇编代码。

以这段代码为例:

- (BOOL)application:(UIApplication*)application 
        didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
   self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
   self.window.backgroundColor = [UIColor whiteColor];
   [self.window makeKeyAndVisible];
   return YES;
}

编译后将生成以下程序集(使用 IDA 分析):

-[SomeAppDelegate 应用程序:didFinishLaunchingWithOptions:]:
    推 {r4-r7,lr}
    添加 r7, sp, #0xC
    str.w r8, [sp,-#0x4]!
    子 sp, sp, #0x18
    movw r1, #(0x343c - 0x2574) ; @选择器(分配)
    移动 r8, r0
    movt.w r1, #0
    mov r0, (0x3464 - 0x2576) ; _OBJC_CLASS_$_UIWindow
    添加 r1, 电脑
    添加 r0, 个人计算机
    ldr r1, [r1]
    ldr r0, [r0]
    blx _objc_msgSend
    移动 r1, (0x3440 - 0x258e) ; @选择器(主屏幕)
    移动 r6, r0
    movw r0, #(0x3468 - 0x2594) ; _OBJC_CLASS_$_UIScreen
    添加 r1, 电脑
    movt.w r0, #0
    添加 r0, 个人计算机
    ldr r1, [r1]
    ldr r0, [r0]
    blx _objc_msgSend
    移动 r7, r7
    blx _objc_retainAutoreleasedReturnValue
    移动 r5, r0
    cbz r5, L25ba
    movw r0, #(0x3444 - 0x25b2) ; @选择器(边界)
    移动 r1, r5
    movt.w r0, #0
    添加 r0, 个人计算机
    ldr r2, [r0]
    添加 r0, sp, #0x8
    blx _objc_msgSend_stret
    b L25c4

L25ba:
    添加 r0, sp, #0x8
    vmov.i32 q8, #0x80
    vstmia r0, {d16-d17}

L25c4:
    mov r1, (0x3448 - 0x25d2) ; @selector(initWithFrame:)
    ldr r0, [sp,#0x10]
    添加 r1, 电脑
    ldr r2, [sp,#0x8]
    ldr r3, [sp,#0xc]
    ldr r4, [sp,#0x14]
    stmea.w sp, {r0,r4}
    移动 r0, r6
    ldr r1, [r1]
    blx _objc_msgSend
    移动 r4, r0
    mov r0, (0x344c - 0x25F2) ; @selector(setWindow:)
    移动 r2, r4
    添加 r0, 个人计算机
    ldr r1, [r0]
    移动 r0, r8
    blx _objc_msgSend
    移动 r0, r4
    blx _objc_release
    移动 r0, r5
    blx _objc_release
    mov r0, (0x3450 - 0x2610) ; @选择器(窗口)
    添加 r0, 个人计算机
    ldr r5, [r0]
    移动 r0, r8
    移动 r1, r5
    blx _objc_msgSend
    移动 r7, r7
    blx _objc_retainAutoreleasedReturnValue
    移动 r1,(0x3454 - 0x2630);@选择器(白色)
    移动 r6, r0
    movw r0, #(0x346C - 0x2636) ; _OBJC_CLASS_$_UIColor
    添加 r1, 电脑
    movt.w r0, #0
    添加 r0, 个人计算机
    ldr r1, [r1]
    ldr r0, [r0]
    blx _objc_msgSend
    移动 r7, r7
    blx _objc_retainAutoreleasedReturnValue
    移动 r4, r0
    mov r0, (0x3458 - 0x2652) ; @selector(setBackgroundColor:)
    移动 r2, r4
    添加 r0, 个人计算机
    ldr r1, [r0]
    移动 r0, r6
    blx _objc_msgSend
    移动 r0, r4
    blx _objc_release
    移动 r0, r6
    blx _objc_release
    移动 r0, r8
    移动 r1, r5
    blx _objc_msgSend
    移动 r7, r7
    blx _objc_retainAutoreleasedReturnValue
    移动 r4, r0
    mov r0, (0x345C - 0x2680) ; @selector(makeKeyAndVisible)
    添加 r0, 个人计算机
    ldr r1, [r0]
    移动 r0, r4
    blx _objc_msgSend
    移动 r0, r4
    blx _objc_release
    移动 r0, #1
    添加 sp, sp, #0x18
    ldr.w r8, [sp], #4
    弹出 {r4-r7,pc}

不细说,你可以看到有很多_objc_release_objc_retainAutoreleasedReturnValue这些是 ARC 在代码生成期间插入的内容。手动反编译,我们会得到:

UIScreen* r5 = objc_retainAutoreleasedReturnValue([UIScreen mainScreen]);
CGRect sp8 = r5 != nil ? [r5 bounds] : CGRectZero;
UIWindow* r4 = [[UIWindow alloc] initWithFrame:sp8];
[self setWindow:r4];
objc_release(r4);
objc_release(r5);

UIWindow* r6a = objc_retainAutoreleasedReturnValue([self window])
UIColor* r4a = objc_retainAutoreleasedReturnValue([UIColor whiteColor])
[r6a setBackgroundColor:r4a];
objc_release(r4a);
objc_release(r6a);

UIWindow* r4b = objc_retainAutoreleasedReturnValue([self window])
[r4b makeKeyAndVisible];
objc_release(r4b);

return 1;

这与@c roald的链接描述的相同。

于 2012-05-03T15:15:38.600 回答
3

Mike Ash 在这里对 ARC 实现进行了非常有启发性的讨论:http: //www.mikeash.com/pyblog/friday-qa-2011-09-30-automatic-reference-counting.html

他在插入 C 函数调用(objc_retain()、objc_release()、objc_retainAutoreleaseReturnValue() 和其他一些函数调用的级别上讨论了它,如果这对你有帮助的话。这样写,编译器可以使用尾调用优化来消除不必要的步骤。

因此,简短的回答是,ARC 没有使用我们在旧版本的 Objecive C 中使用的相同 [retain]/[release] 方法,因此看到 ARC 预处理的代码不一定会指导您如何自己做。

ARC 在编译器中作为预处理步骤实现并不罕见——我相信 Objective C 的许多特性都是以这种方式实现的。

于 2012-05-12T14:09:30.093 回答
0

不,如果不了解 LLVM 的汇编级详细信息,您将无法了解此类详细信息。

于 2012-05-12T16:53:57.733 回答