Truyền một phiên bản của một lớp tới một @protocol trong Objective-C


102

Tôi có một đối tượng (một UIViewController) có thể có hoặc không phù hợp với giao thức mà tôi đã xác định.

Tôi biết mình có thể xác định xem đối tượng có tuân thủ giao thức hay không, sau đó gọi phương thức một cách an toàn:

if([self.myViewController conformsToProtocol:@protocol(MyProtocol)]) {
    [self.myViewController protocolMethod]; // <-- warning here
}

Tuy nhiên, XCode hiển thị cảnh báo:

warning 'UIViewController' may not respond to '-protocolMethod'

Cách thích hợp để ngăn chặn cảnh báo này là gì? Tôi dường như không thể chọn self.myViewControllernhư một MyProtocollớp học.

Câu trả lời:


171

Cách chính xác để làm điều này là làm:

if ([self.myViewController conformsToProtocol:@protocol(MyProtocol)])
{
        UIViewController <MyProtocol> *vc = (UIViewController <MyProtocol> *) self.myViewController;
        [vc protocolMethod];
}

Các UIViewController <MyProtocol> *loại đúc dịch để "vc là một đối tượng UIViewController rằng chiếu theo MyProtocol", trong khi sử dụng id <MyProtocol>dịch để "vc là một đối tượng của một lớp chưa biết rằng chiếu theo MyProtocol".

Bằng cách này, trình biên dịch sẽ cung cấp cho bạn việc kiểm tra kiểu thích hợp vc- trình biên dịch sẽ chỉ đưa ra cảnh báo cho bạn nếu bất kỳ phương thức nào không được khai báo UIViewControllerhoặc <MyProtocol>được gọi. idchỉ nên được sử dụng trong trường hợp bạn không biết lớp / kiểu của đối tượng được ép kiểu.


2
Khi sử dụng các giao thức, bạn thực sự không nên quan tâm đến loại đối tượng - toàn bộ điểm của một giao thức là bất kỳ loại đối tượng nào cũng có thể chấp nhận nó và được sử dụng mà không cần phải truyền đến đối tượng cụ thể. Vì vậy, tôi khuyên bạn nên sử dụng câu trả lời của @andy ở bất kỳ nơi nào bạn đang truyền tới một giao thức thay vì ở trên - id<MyProtocol> p = (id<MyProtocol>)self.myViewController;Câu trả lời này và @andys đều đúng, nhưng câu trả lời của anh ấy đúng hơn .
memmons

2
@Answerbot nhận xét của bạn không chính xác và bỏ sót ý tôi đã đưa ra trong đoạn cuối câu trả lời của mình. Bạn có thể quan tâm hoặc không quan tâm đến loại đối tượng, điều đó tùy thuộc vào tình huống. Chuyện gì xảy ra nếu bạn muốn gửi một thông điệp tuyên bố trên UIViewControllerđể vctrong ví dụ trong câu trả lời của tôi, và nó khai báo là id <MyProtocol>?
Nick Forge

Không chắc những gì liên quan đến nhận xét của tôi là không chính xác? Trong mọi trường hợp, nếu bạn đang kiểm tra xem một đối tượng có tuân theo một giao thức hay không, tại sao bạn lại gọi một số phương thức khác không liên quan đến giao thức? Tôi không thể nhớ đã bao giờ cần làm điều này hoặc thấy điều này trong mã mà tôi đã xem xét. Có vẻ giống như một mùi mã đối với tôi.
memmons

Chỉ vì bạn chưa nhìn thấy / sử dụng nó, không có nghĩa là nó có mùi mã. Đây là một đoạn mã hiển thị một ví dụ về việc loại bỏ thông tin loại bằng cách sử dụng idlà một vấn đề: gist.github.com/nsforge/7743616
Nick Forge

60

Bạn có thể truyền nó như thế này:

if([self.myViewController conformsToProtocol:@protocol(MyProtocol)])
{
    id<MyProtocol> p = (id<MyProtocol>)self.myViewController;
    [p protocolMethod];
}

Điều này cũng khiến tôi bị thương. Trong Objective-C, bản thân giao thức không phải là loại, vì vậy bạn cần chỉ định id(hoặc một số loại khác, chẳng hạn như NSObject) cùng với giao thức mà bạn muốn.


Ah, tuyệt, cảm ơn. Tôi vừa kiểm tra và thấy rằng việc đúc nó cũng (id)hoạt động. Đó có phải là hình thức xấu?
Ford

1
Nếu bạn ép kiểu id <MyProtocol> thì trình biên dịch sẽ cảnh báo bạn nếu bạn sử dụng các phương thức không được xác định trong giao thức đó.
dreamlax

1
@dreamlax - Đây là cách trình biên dịch thực hiện kiểm tra loại đối với các giao thức. Xem developer.apple.com/documentation/Cocoa/Conceptual/ObjectiveC/… để biết thêm thông tin.
Andy

1
@Ford - sẽ tốt hơn nếu sử dụng giao thức cụ thể, vì theo cách đó trình biên dịch có thể thực hiện một số kiểm tra kiểu cho bạn.
Andy

1
@Andy, tôi không nghĩ bạn cần dấu '*' vì 'id' đã là một con trỏ. Vì vậy: id <MyProtocol> p = (id <MyProtocol>) self.myViewController; [p giao thứcMethod]; Hoặc chỉ: [(id <MyProtocol>) self.myViewController protocolMethod];
Ford
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.