2009-09-11 25 views
7

tôi đã muốn kết hợp một tập hợp các hình chữ nhật với các hành động tương ứng, vì vậy tôi cố gắng làmTôi có thể đặt bộ chọn mục tiêu-C trong cấu trúc không?

struct menuActions { 
    CGRect rect; 
    SEL action; 
}; 

struct menuActions someMenuRects[] = { 
    { { { 0, 0 }, {320, 60 } }, @selector(doSomething) }, 
    { { { 0, 60}, {320, 50 } }, @selector(doSomethingElse) }, 
}; 

nhưng tôi nhận được lỗi "yếu tố khởi tạo phải là không đổi". Có một số lý do mà những gì tôi đang cố gắng làm không được phép nói chung, hoặc không được phép ở phạm vi toàn cầu, hay tôi có một số loại sai câu nhỏ?

+2

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

+0

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]} –

+2

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

Trả lời

23

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_4L_OBJC_SELECTOR_REFERENCES_5. Các văn bản .objc_meth_var_names.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 ASCIIC 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.

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!

+1

+1. Câu trả lời chính xác. –

+0

cảm ơn các chi tiết. Tôi đã giả định rằng sel_registerName xảy ra tại thời gian biên dịch/liên kết, thay vì tại thời gian tải, cho một bộ chọn liên tục ... vì vậy tôi đoán sel_registerName thực sự chỉ "chậm" nếu bạn gọi nó> 1 lần ... –

+0

Wow .. không hiểu một nửa, nhưng nghe như bạn biết bạn đang nói gì) +1 –

4

Làm thế nào về:

struct menuActions { 
    CGRect rect; 
    const char *action; 
}; 

struct menuActions someMenuRects[] = { 
    { { { 0, 0 }, {320, 60 } }, "doSomething" }, 
    { { { 0, 60}, {320, 50 } }, "doSomethingElse" }, 
}; 

Khi chạy, đăng ký selectors:

int numberOfActions = 2; 
for (int i=0; i < numberOfActions; i++) 
    NSLog (@"%s", sel_registerName(someMenuRects[i].action)); 

Output:

[Session started at 2009-09-11 16:16:12 -0700.] 
2009-09-11 16:16:14.527 TestApp[12800:207] @selector(doSomething) 
2009-09-11 16:16:14.531 TestApp[12800:207] @selector(doSomethingElse) 

Thông tin thêm về tại Objective-C 2.0 Runtime Reference.

+0

+1 cho sự thông minh –

+0

vâng, tôi đã biết về sel_registerName, nhưng về nguyên tắc lại có vẻ sai khi thực hiện bước này khi chạy khi nó thực sự được xác định tại thời gian biên dịch. có lẽ câu trả lời cho câu hỏi của tôi thực sự chỉ là "không, bạn không thể", và tôi nên hỏi một câu hỏi khác như "cách tốt nhất để xây dựng một bảng điều phối hàm trong Mục tiêu-C" là gì ... –

+1

Tôi không có ý giả sử sự thiếu hiểu biết về phía bạn. Đó là một câu hỏi thú vị, nhưng tôi không biết câu trả lời đúng. –

-1

Có vẻ như bạn đang phát minh lại NSCell tại đây. Nếu bạn muốn triển khai thực đơn, tại sao không sử dụng các lớp UI hiện có?

+1

iPhone có NSCell không? –

Các vấn đề liên quan