Câu trả lời này là lý do tại sao "initializer element is not constant"
.
Với ví dụ sau:
SEL theSelector; // Global variable
void func(void) {
theSelector = @selector(constantSelector:test:);
}
Biên dịch một cái gì đó như thế này cho các i386
kiến trúc:
.objc_meth_var_names
L_OBJC_METH_VAR_NAME_4:
.ascii "constantSelector:test:\0"
.objc_message_refs
.align 2
L_OBJC_SELECTOR_REFERENCES_5:
.long L_OBJC_METH_VAR_NAME_4
phần này định nghĩa hai 'biến' địa phương (về mã lắp ráp) (trên thực tế nhãn), L_OBJC_METH_VAR_NAME_4
và L_OBJC_SELECTOR_REFERENCES_5
. Các văn bản .objc_meth_var_names
và .objc_message_refs
, ngay trước nhãn 'biến', cho trình biên dịch biết phần nào của tệp đối tượng để đặt "nội dung tiếp theo". Các phần có ý nghĩa với trình liên kết. L_OBJC_SELECTOR_REFERENCES_5
ban đầu được đặt thành địa chỉ của L_OBJC_METH_VAR_NAME_4
.
Lúc tải thực hiện, trước khi chương trình bắt đầu thực hiện, mối liên kết làm điều gì đó xấp xỉ như thế này:
- lặp trên mỗi mục trong phần
.objc_message_refs
.
- Mỗi mục nhập ban đầu được đặt thành con trỏ đến một sốbị chấm dứt
C
.
- Trong ví dụ của chúng tôi, con trỏ ban đầu được thiết lập đến địa chỉ của
L_OBJC_METH_VAR_NAME_4
, mà chứa ASCII
C
chuỗi "constantSelector:test:"
.
- Sau đó, thực hiện
sel_registerName("constantSelector:test:")
và lưu trữ giá trị trả lại tại L_OBJC_SELECTOR_REFERENCES_5
. Trình liên kết, biết chi tiết triển khai riêng tư, không được gọi theo nghĩa đen.
Về cơ bản các mối liên kết thực hiện này tại thời gian tải ví dụ của chúng tôi:
L_OBJC_SELECTOR_REFERENCES_5 = sel_registerName("constantSelector:test:");
Đây là lý do tại sao "initializer element is not constant"
- yếu tố initializer phải được liên tục tại thời gian biên dịch. Giá trị không thực sự được biết cho đến khi chương trình bắt đầu thực hiện. Ngay cả khi đó, các khai báo struct
của bạn được lưu trữ trong phần liên kết khác, phần .data
. Trình liên kết chỉ biết cách cập nhật giá trị SEL
trong phần .objc_message_refs
và không có cách nào để 'sao chép' thời gian chạy được tính SEL
giá trị từ .objc_message_refs
đến một số vị trí tùy ý trong .data
.
Mã C
nguồn ...
theSelector = @selector(constantSelector:test:);
... trở thành:
movl L_OBJC_SELECTOR_REFERENCES_5, %edx // The SEL value the linker placed there.
movl L_theSelector$non_lazy_ptr, %eax // The address of theSelector.
movl %edx, (%eax) // theSelector = L_OBJC_SELECTOR_REFERENCES_5;
Kể từ khi mối liên kết làm tất cả công việc của mình trước khi chương trình đang thực hiện, L_OBJC_SELECTOR_REFERENCES_5
chứa cùng giá trị chính xác mà bạn sẽ nhận được nếu bạn gọi sel_registerName("constantSelector:test:")
:
theSelector = sel_registerName("constantSelector:test:");
Sự khác biệt là đây là một cuộc gọi chức năng, và chức năng cần phải làm công việc thực tế của việc tìm kiếm nếu nó đã được đăng ký, hoặc đi qua quá trình phân bổ một giá trị SEL
mới để đăng ký bộ chọn. Đây là chậm hơn đáng kể mà chỉ cần tải một giá trị không đổi. Mặc dù đây là 'chậm hơn', nó cho phép bạn vượt qua một chuỗi C
tùy ý. Điều này có thể hữu ích nếu:
- Bộ chọn không được biết lúc biên dịch.
- Bộ chọn không được biết cho đến trước khi được gọi.
- Bạn cần phải thay đổi bộ chọn động theo thời gian chạy.
Tất cả các bộ chọn cần phải đi qua , mỗi lần đăng ký chính xác SEL
. Điều này có lợi thế là có chính xác một giá trị, ở mọi nơi, cho bất kỳ bộ chọn nào đã cho. Mặc dù một chi tiết riêng tư thực hiện, SEL
là "thường" chỉ là một con trỏ char *
đến một bản sao của các bộ chọn C
văn bản chuỗi.
Bây giờ bạn đã biết. Và biết là một nửa trận chiến!
Tôi không biết tại sao '@ selector' không phải là hằng số, nhưng nếu bạn có thể đặt khởi tạo vào một hàm, bạn sẽ không phải lo lắng về điều đó. – Amok
Tôi không muốn viết mã như someMenuRects [0] .action = @doSomething, bởi vì sau đó tôi cũng có thể làm điều tương tự ở thời gian chạy, tức là nếu (CGRectContainsPoint (someMenuRects [0], pt)) {[self doSomething]} –
Bộ chọn không phải là hằng số vì giá trị không thực sự được xác định cho đến rất sớm khi chạy. Vì vậy, bạn có thể dính một chuỗi vào đó và thực hiện tra cứu tại thời gian chạy, nếu bạn muốn. – bbum