Ответ 1
ARC в clang не работает, переписывая код из ObjC в ObjC, но выдавая дополнительный бит LLVM сохранения/выпуска во время кода-gen. Это означает, что невозможно понять, как компилятор "исправит" его, не перейдя на уровень LLVM IR/assembly.
Если ARC испускает бит-код LLVM, как вы сказали. Это сделано для этой цели, которая использует меньше времени в процессе компиляции? (менее сложный код ObjC, меньше заголовочный файл?)
Всегда лучше, если компилятор может уменьшить количество проходов через код.
Можете ли вы показать мне пример или утилиту, отображающую код на уровне сборки?
Чтобы получить код сборки, вы можете либо
-
Создайте сборку непосредственно из компилятора. В командной строке добавьте флаг
-S
при вызове компилятора. Результатом является файл.S
, содержащий код сборки. В проекте Xcode откройте файл исходного кода, затем перейдите в Продукт (в строке меню) → Создать вывод → Файл сборки.. p > -
Сгенерируйте объектный файл, а затем разберите его. Встроенная команда
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 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}
Не вдаваясь в подробности, вы можете увидеть, что существует много _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.