2011-01-01 36 views
18

Từ những gì tôi đã học được cho đến nay: Trong Mục tiêu-C, bạn có thể gửi bất kỳ thông điệp nào đến bất kỳ đối tượng nào. Nếu đối tượng thực hiện đúng phương thức, nó sẽ được thực hiện nếu không sẽ không có gì xảy ra. Điều này là do trước khi tin nhắn được gửi Mục tiêu-C sẽ thực hiện respondsToSelector.Mục tiêu-C respondsToSelector

Tôi hy vọng tôi đã đúng cho đến nay.

Tôi đã thực hiện một chương trình nhỏ để kiểm tra nơi một hành động được gọi mỗi khi một thanh trượt được di chuyển. Ngoài ra để thử nghiệm tôi đặt người gửi đến NSButton nhưng trên thực tế nó là một NSSlider. Bây giờ tôi đã hỏi đối tượng nếu nó sẽ trả lời setAlternateTitle. Trong khi một NSButton sẽ làm và NSSlider sẽ không. Nếu tôi chạy mã và làm respondsToSelector bản thân mình, nó sẽ cho tôi biết đối tượng sẽ không phản hồi với bộ chọn đó. Nếu tôi thử nghiệm một cái gì đó khác như intValue, nó sẽ phản hồi. Vì vậy, mã của tôi là tốt cho đến nay.

- (IBAction)sliderDidMove:(id)sender 
{ 
    NSButton *slider = sender; 

    BOOL responds = 
    [slider respondsToSelector:@selector(setAlternateTitle)]; 

    if(responds == YES) 
    { 
     NSLog(@"YES");   
    } 
    else 
    { 
     NSLog(@"NO"); 
    } 

    [slider setAlternateTitle:@"Hello World"]; 
} 

Nhưng khi tôi thực sự gửi thông báo setAlternateTitle chương trình sẽ bị lỗi và tôi không chắc chắn lý do tại sao. Không nên làm một respondsToSelector trước khi gửi tin nhắn?

Trả lời

146

Trước hết, tên của một phương thức (bộ chọn của nó) bao gồm tất cả các phần con và các ký tự dấu hai chấm, như mvd cho biết. Trước hết, phương thức -respondsToSelector: không được gọi bởi thời gian chạy, nó thường được gọi bởi người dùng (chính bạn hoặc các API muốn biết liệu một đại biểu, ví dụ, có phản hồi phương thức tùy chọn của giao thức) hay không.

Khi bạn gửi thư đến một đối tượng, thời gian chạy sẽ tìm cách triển khai phương thức trong lớp của đối tượng (thông qua con trỏ isa của đối tượng). Nó tương đương với việc gửi -respondsToSelector: mặc dù bản thân thông báo không được gửi đi. Nếu việc thực hiện phương thức được tìm thấy trong lớp hoặc trong các lớp siêu lớp của nó, nó được gọi với tất cả các đối số bạn đã nhập.

Nếu không, thì thời gian chạy sẽ cho cơ hội thứ hai được thực thi. Nó sẽ bắt đầu bằng cách gửi thông báo + (BOOL)resolveInstanceMethod:(SEL)name đến lớp của đối tượng: phương thức này cho phép bạn thêm phương thức vào thời gian chạy tới lớp: nếu thông báo này trả về CÓ, điều đó có nghĩa là nó có thể lấy lại thông báo.

Nếu không, nó sẽ gửi thông báo đến cơ quan thứ ba, nó sẽ gửi - (id)forwardingTargetForSelector:(SEL)aSelector với bộ chọn, phương pháp này có thể trả về một đối tượng khác có thể phản hồi bộ chọn thay mặt người nhận thực. có thể trả lời, phương thức được thực hiện và giá trị được trả về như thể nó được trả về bởi thông báo gốc. (Lưu ý: Tính năng này khả dụng bắt đầu với OS X 10.6 hoặc iOS 4.)

Nếu đối tượng được trả về là không hoặc tự (để tránh vòng lặp vô hạn), thời gian chạy sẽ cho cơ hội thứ tư thực thi phương thức… thông báo - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector để nhận chữ ký phương thức để tạo lời gọi. Nếu được cung cấp thì một lời gọi được gửi qua thông báo - (void)forwardInvocation:(NSInvocation *)anInvocation. Trong phương thức này, bạn có thể phân tích cú pháp lời gọi và xây dựng các tin nhắn khác để gửi đến các mục tiêu khác theo bất kỳ cách nào bạn muốn, và sau đó bạn có thể đặt giá trị trả về của lời gọi ... Giá trị đó sẽ hoạt động như giá trị trả về của thư gốc.

Cuối cùng, nếu không có chữ ký phương thức được trả về bởi đối tượng, thì thời gian chạy gửi thông báo - (void)doesNotRecognizeSelector:(SEL)aSelector đến đối tượng của bạn, việc triển khai phương thức này trong lớp NSObject sẽ ném ra một ngoại lệ.

+0

+1 cho giải thích tuyệt vời này. Bạn có thể cung cấp một số liên kết trong đó toàn bộ quá trình này được ghi lại chi tiết không. Đó là giá trị đọc. – taskinoor

+4

Các tài liệu có tại đây: http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Introduction/Introduction.html một phần ... Các nội dung khác nằm trong tham chiếu lớp NSObject – Psycho

+1

bằng cách này , 'forwardingTargetForSelector:' chỉ trong Mac OS X 10.6+ hoặc iOS 4+ – user102008

7

Đối với một điều, selector không chỉ là "tên" của thư, mà còn là những gì sau, tức là các đối số và tên của chúng.

Vì vậy, chọn đúng đối với một số -(void)setAlternateTitle:(NSString*)str sẽ

@selector(setAlternateTitle:) 

với các :

Đối với vấn đề của bạn: Nếu một lớp respondsToSelector() và bạn thực hiện chọn đó, bạn không nên có được một sự cố khi gửi bộ chọn không xác định. Bạn nhìn thấy loại nhật ký sự cố nào trong cửa sổ gỡ lỗi?

(ps. Tại sao không bao gồm các [slider setAlternateTitle:...] trong khối có điều kiện if (responds) { ... }?)

+0

Chính xác hơn, công cụ chọn không bao gồm _ “nội dung sau, nghĩa là đối số và tên của chúng” _, nhưng “số và vị trí của đối số trong tên thông báo”. –

2

"Đây là bởi vì trước khi được thông báo là gửi Objective-C sẽ thực hiện respondsToSelector."

Tôi đoán điều này là không chính xác. Nếu đối tượng không phản hồi với bộ chọn, đối tượng sẽ bị lỗi khi chạy. Không có hệ thống kiểm tra tự động. Nếu có một kiểm tra bởi hệ thống thời gian chạy sau đó chúng ta sẽ không bao giờ nhận được "không được công nhận chọn gửi đến trường hợp" ngoại lệ.

Hãy làm cho tôi chính xác nếu tôi sai.

EDIT: Đây không phải là lỗi chuyển tiếp thẳng, nhưng kết quả mặc định là quá trình sẽ bị chấm dứt. Toàn bộ trình tự đã được giải thích trong bình luận và câu trả lời khác, vì vậy tôi sẽ không viết lại điều đó.

+2

Nó không sụp đổ, nó rơi trở lại một quá trình gọi là * forwarding *. Thật không may, điều này dường như không được tài liệu tốt, nhưng nó có một số điều: đầu tiên nó gọi là '+ resolveInstanceMethod:' trên lớp của người nhận. Nếu điều này không thành công, nó sẽ gọi '-forwardingTargetForSelector:', sau đó là '-forwardInvocation:'. Việc triển khai mặc định của '-forwardInvocation:' ném một ngoại lệ bộ chọn chưa được thực hiện, điều này không giống với sự cố. –

+0

@Ahruman, cảm ơn lời giải thích. Tôi không biết chuỗi này. Nó không thực sự là một tai nạn thẳng về phía trước như lỗi phân đoạn, nhưng kết quả mặc định dường như quá trình sẽ bị chấm dứt. – taskinoor

+0

Ít nhất tôi đã đúng rằng phương pháp này không được hệ thống tự động gọi. – taskinoor

1

Có phương thức +instancesRespondToSelector:. Như tên cho thấy, nó cho bạn biết liệu các cá thể của lớp có triển khai phương thức đó hay không.

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