I have read the Transitioning to ARC Release Notes in "Summary" Section. They told :
ARC works by adding code at compile time to ensure that objects live
as long as necessary, but no longer. Conceptually, it follows the same
memory management conventions as manual reference counting (described
in Advanced Memory Management Programming Guide) by adding the
appropriate memory management calls for you.
In order for the compiler to generate correct code
I wonder what result that ARC corrected our code.
My question : Can we see the change? (In the term of alloc , retain , assign or release .Not assembly level !)
Reason : Because I think It's good to see the best-practice code in the old tradition development without ARC mode.
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.
Mike Ash has a very illuminating discussion of ARC implementation here: http://www.mikeash.com/pyblog/friday-qa-2011-09-30-automatic-reference-counting.html
He discusses it at the level of inserting C function calls (objc_retain(), objc_release(), objc_retainAutoreleaseReturnValue(), and a few others, if that would be helpful for you. Written that way, the compiler can use tail-call optimizations to eliminate unnecessary steps.
The short answer, therefore, is that ARC doesn't use the same [retain]/[release] methods we would have used in older versions of Objecive C, so therefore seeing the ARC-preprocessed code wouldn't necessarily instruct you in how to do it yourself.
ARC is not unusual in being implemented as a preprocessing step in the compiler -- I believe many features of Objective C are implemented this way.