ARC in clang doesn't work by rewriting code from ObjC to ObjC, but emitting the extra retain/release LLVM bitcode during code-gen. That means, it is not possible to know how the compiler "fix" it without going to LLVM IR / assembly level.
If ARC emit the LLVM bitcode as you said. Is it made for the purpose that use less time in compile process? (less complex ObjC code, less header file?)
It is always better if the compiler can reduce the number of passes through the code.
Can you show me some example or utility that show the code in assembly level?
To get the assembly code, you could either
Generate assembly directly from the compiler. In the command line, add the -S
flag when invoking the compiler. The result is a .S
file containing the assembly code. In an Xcode project, open the source code file, then go to Product (on menu bar) → Generate Output → Assembly File.
Generate the object file, and then disassemble it. The built-in command otool -tvV <file>
can perform disassembly, and there are advanced tools like otx (free) or IDA (free for evaluation).
I prefer route 2 because it generates less garbage and the disassembly tool can be configured to produce more useful information. Anyway, with either method you need to be able to read assembly code.
Take this code as example:
- (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;
}
After compiling the following assembly will be produced (analyzed using IDA):
-[SomeAppDelegate application:didFinishLaunchingWithOptions:]:
push {r4-r7,lr}
add r7, sp, #0xC
str.w r8, [sp,-#0x4]!
sub sp, sp, #0x18
movw r1, #(0x343c - 0x2574) ; @selector(alloc)
mov r8, r0
movt.w r1, #0
mov r0, (0x3464 - 0x2576) ; _OBJC_CLASS_$_UIWindow
add r1, pc
add r0, pc
ldr r1, [r1]
ldr r0, [r0]
blx _objc_msgSend
mov r1, (0x3440 - 0x258e) ; @selector(mainScreen)
mov r6, r0
movw r0, #(0x3468 - 0x2594) ; _OBJC_CLASS_$_UIScreen
add r1, pc
movt.w r0, #0
add r0, pc
ldr r1, [r1]
ldr r0, [r0]
blx _objc_msgSend
mov r7, r7
blx _objc_retainAutoreleasedReturnValue
mov r5, r0
cbz r5, L25ba
movw r0, #(0x3444 - 0x25b2) ; @selector(bounds)
mov r1, r5
movt.w r0, #0
add r0, pc
ldr r2, [r0]
add r0, sp, #0x8
blx _objc_msgSend_stret
b L25c4
L25ba:
add r0, sp, #0x8
vmov.i32 q8, #0x80
vstmia r0, {d16-d17}
L25c4:
mov r1, (0x3448 - 0x25d2) ; @selector(initWithFrame:)
ldr r0, [sp,#0x10]
add r1, pc
ldr r2, [sp,#0x8]
ldr r3, [sp,#0xc]
ldr r4, [sp,#0x14]
stmea.w sp, {r0,r4}
mov r0, r6
ldr r1, [r1]
blx _objc_msgSend
mov r4, r0
mov r0, (0x344c - 0x25F2) ; @selector(setWindow:)
mov r2, r4
add r0, pc
ldr r1, [r0]
mov r0, r8
blx _objc_msgSend
mov r0, r4
blx _objc_release
mov r0, r5
blx _objc_release
mov r0, (0x3450 - 0x2610) ; @selector(window)
add r0, pc
ldr r5, [r0]
mov r0, r8
mov r1, r5
blx _objc_msgSend
mov r7, r7
blx _objc_retainAutoreleasedReturnValue
mov r1, (0x3454 - 0x2630) ; @selector(whiteColor)
mov r6, r0
movw r0, #(0x346C - 0x2636) ; _OBJC_CLASS_$_UIColor
add r1, pc
movt.w r0, #0
add r0, pc
ldr r1, [r1]
ldr r0, [r0]
blx _objc_msgSend
mov r7, r7
blx _objc_retainAutoreleasedReturnValue
mov r4, r0
mov r0, (0x3458 - 0x2652) ; @selector(setBackgroundColor:)
mov r2, r4
add r0, pc
ldr r1, [r0]
mov r0, r6
blx _objc_msgSend
mov r0, r4
blx _objc_release
mov r0, r6
blx _objc_release
mov r0, r8
mov r1, r5
blx _objc_msgSend
mov r7, r7
blx _objc_retainAutoreleasedReturnValue
mov r4, r0
mov r0, (0x345C - 0x2680) ; @selector(makeKeyAndVisible)
add r0, pc
ldr r1, [r0]
mov r0, r4
blx _objc_msgSend
mov r0, r4
blx _objc_release
movs r0, #1
add sp, sp, #0x18
ldr.w r8, [sp], #4
pop {r4-r7,pc}
Without going into detail, you can see there are many _objc_release
and _objc_retainAutoreleasedReturnValue
. These are what ARC inserts during code-gen. Decompiling it by hand, we'll get:
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;
which is just the same as what @c roald's link describes.