Tôi hơi bối rối là tại sao kêu vang là phát ra mã khác nhau cho hai phương pháp sau đây:Tại sao các phương thức đơn giản này biên dịch khác nhau?
@interface ClassA : NSObject
@end
@implementation ClassA
+ (ClassA*)giveMeAnObject1 {
return [[ClassA alloc] init];
}
+ (id)giveMeAnObject2 {
return [[ClassA alloc] init];
}
@end
Nếu chúng ta nhìn vào ARMv7 phát ra thì chúng ta thấy điều này, tại O3
, với ARC được kích hoạt:
.align 2
.code 16
.thumb_func "+[ClassA giveMeAnObject1]"
"+[ClassA giveMeAnObject1]":
push {r7, lr}
movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_-(LPC0_0+4))
mov r7, sp
movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_-(LPC0_0+4))
movw r0, :lower16:(L_OBJC_CLASSLIST_REFERENCES_$_-(LPC0_1+4))
movt r0, :upper16:(L_OBJC_CLASSLIST_REFERENCES_$_-(LPC0_1+4))
LPC0_0:
add r1, pc
LPC0_1:
add r0, pc
ldr r1, [r1]
ldr r0, [r0]
blx _objc_msgSend
movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_2-(LPC0_2+4))
movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_2-(LPC0_2+4))
LPC0_2:
add r1, pc
ldr r1, [r1]
blx _objc_msgSend
pop.w {r7, lr}
b.w _objc_autorelease
.align 2
.code 16
.thumb_func "+[ClassA giveMeAnObject2]"
"+[ClassA giveMeAnObject2]":
push {r7, lr}
movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_-(LPC2_0+4))
mov r7, sp
movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_-(LPC2_0+4))
movw r0, :lower16:(L_OBJC_CLASSLIST_REFERENCES_$_-(LPC2_1+4))
movt r0, :upper16:(L_OBJC_CLASSLIST_REFERENCES_$_-(LPC2_1+4))
LPC2_0:
add r1, pc
LPC2_1:
add r0, pc
ldr r1, [r1]
ldr r0, [r0]
blx _objc_msgSend
movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_2-(LPC2_2+4))
movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_2-(LPC2_2+4))
LPC2_2:
add r1, pc
ldr r1, [r1]
blx _objc_msgSend
pop.w {r7, lr}
b.w _objc_autoreleaseReturnValue
sự khác biệt chỉ là tiếng gọi đuôi để objc_autoreleaseReturnValue
vs objc_autorelease
. Tôi mong chờ cả hai sẽ gọi số objc_autoreleaseReturnValue
. Trong thực tế, phương pháp đầu tiên không sử dụng objc_autoreleaseReturnValue
có nghĩa là nó sẽ có khả năng chậm hơn giây vì chắc chắn sẽ có một autorelease sau đó được giữ lại bởi người gọi, thay vì bỏ qua nhanh hơn cuộc gọi dư thừa này mà ARC có thể thực hiện nếu nó được hỗ trợ thời gian chạy.
Các LLVM mà được phát ra cho một số loại lý do tại sao nó là như thế:
define internal %1* @"\01+[ClassA giveMeAnObject1]"(i8* nocapture %self, i8* nocapture %_cmd) {
%1 = load %struct._class_t** @"\01L_OBJC_CLASSLIST_REFERENCES_$_", align 4
%2 = load i8** @"\01L_OBJC_SELECTOR_REFERENCES_", align 4
%3 = bitcast %struct._class_t* %1 to i8*
%4 = tail call i8* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to i8* (i8*, i8*)*)(i8* %3, i8* %2)
%5 = load i8** @"\01L_OBJC_SELECTOR_REFERENCES_2", align 4
%6 = tail call i8* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to i8* (i8*, i8*)*)(i8* %4, i8* %5)
%7 = tail call i8* @objc_autorelease(i8* %6) nounwind
%8 = bitcast i8* %6 to %1*
ret %1* %8
}
define internal i8* @"\01+[ClassA giveMeAnObject2]"(i8* nocapture %self, i8* nocapture %_cmd) {
%1 = load %struct._class_t** @"\01L_OBJC_CLASSLIST_REFERENCES_$_", align 4
%2 = load i8** @"\01L_OBJC_SELECTOR_REFERENCES_", align 4
%3 = bitcast %struct._class_t* %1 to i8*
%4 = tail call i8* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to i8* (i8*, i8*)*)(i8* %3, i8* %2)
%5 = load i8** @"\01L_OBJC_SELECTOR_REFERENCES_2", align 4
%6 = tail call i8* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to i8* (i8*, i8*)*)(i8* %4, i8* %5)
%7 = tail call i8* @objc_autoreleaseReturnValue(i8* %6) nounwind
ret i8* %6
}
Nhưng tôi phải vật lộn để xem tại sao nó đã quyết định biên soạn hai phương pháp này khác nhau. Có ai có thể làm sáng tỏ nó không?
Cập nhật:
thậm chí lạ là những phương pháp khác:
+ (ClassA*)giveMeAnObject3 {
ClassA *a = [[ClassA alloc] init];
return a;
}
+ (id)giveMeAnObject4 {
ClassA *a = [[ClassA alloc] init];
return a;
}
Những biên dịch để:
.align 2
.code 16
.thumb_func "+[ClassA giveMeAnObject3]"
"+[ClassA giveMeAnObject3]":
push {r4, r7, lr}
movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_-(LPC2_0+4))
add r7, sp, #4
movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_-(LPC2_0+4))
movw r0, :lower16:(L_OBJC_CLASSLIST_REFERENCES_$_-(LPC2_1+4))
movt r0, :upper16:(L_OBJC_CLASSLIST_REFERENCES_$_-(LPC2_1+4))
LPC2_0:
add r1, pc
LPC2_1:
add r0, pc
ldr r1, [r1]
ldr r0, [r0]
blx _objc_msgSend
movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_2-(LPC2_2+4))
movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_2-(LPC2_2+4))
LPC2_2:
add r1, pc
ldr r1, [r1]
blx _objc_msgSend
blx _objc_retainAutoreleasedReturnValue
mov r4, r0
mov r0, r4
blx _objc_release
mov r0, r4
pop.w {r4, r7, lr}
b.w _objc_autoreleaseReturnValue
.align 2
.code 16
.thumb_func "+[ClassA giveMeAnObject4]"
"+[ClassA giveMeAnObject4]":
push {r4, r7, lr}
movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_-(LPC3_0+4))
add r7, sp, #4
movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_-(LPC3_0+4))
movw r0, :lower16:(L_OBJC_CLASSLIST_REFERENCES_$_-(LPC3_1+4))
movt r0, :upper16:(L_OBJC_CLASSLIST_REFERENCES_$_-(LPC3_1+4))
LPC3_0:
add r1, pc
LPC3_1:
add r0, pc
ldr r1, [r1]
ldr r0, [r0]
blx _objc_msgSend
movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_2-(LPC3_2+4))
movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_2-(LPC3_2+4))
LPC3_2:
add r1, pc
ldr r1, [r1]
blx _objc_msgSend
blx _objc_retainAutoreleasedReturnValue
mov r4, r0
mov r0, r4
blx _objc_release
mov r0, r4
pop.w {r4, r7, lr}
b.w _objc_autoreleaseReturnValue
Lần này, họ tuy nhiên giống hệt nhau có một vài điều mà có thể được tối ưu hóa nhiều hơn tại đây:
Có dư thừa
mov r4, r0
theo sau làmov r0, r4
.Có một lần lưu giữ theo sau là bản phát hành.
Chắc chắn, các bit dưới cùng của cả hai các phương pháp đó có thể biến thành:
LPC3_2:
add r1, pc
ldr r1, [r1]
blx _objc_msgSend
pop.w {r4, r7, lr}
b.w _objc_autoreleaseReturnValue
Rõ ràng chúng ta có thể sau đó cũng bỏ qua popping r4
vì chúng ta không thực sự clobber nó nữa. Sau đó, phương pháp sẽ biến thành chính xác giống như giveMeAnObject2
đó là chính xác những gì chúng tôi mong đợi.
Tại sao tiếng kêu không thông minh và làm điều này ?!
Rất tiếc, có. Bạn đã thử các cấp độ tối ưu hóa khác nhau chưa? -Os? Trong mọi trường hợp, hãy gửi một lỗi (và đăng # ở đây). – bbum
Tôi đã thử các mức tối ưu hóa khác nhau nhưng không có gì thực sự bổ sung để báo cáo về nó. Mặc dù tôi vẫn cần phải làm việc thông qua nó nhiều hơn một chút. Vâng, tôi sẽ gửi một lỗi, nhưng tôi muốn đảm bảo rằng nó thực sự là một lỗi đầu tiên. Tôi có thể chỉ thiếu một cái gì đó đơn giản, mặc dù nó rất lạ. – mattjgalloway
@bbum - Thực ra, tôi đã nói dối. 'O0' hơi thú vị. Trường hợp 1 & 2 trở nên giống nhau. Mặc dù rõ ràng không có tối ưu hóa cuộc gọi đuôi hoặc bất cứ điều gì như thế.Vì vậy, umm, tôi đoán nó là một cái gì đó để làm với tối ưu hóa các cuộc gọi đuôi. – mattjgalloway