ส่งตัวอย่างของคลาสไปยัง @protocol ใน Objective-C


102

ฉันมีวัตถุ (UIViewController) ซึ่งอาจเป็นไปตามหรือไม่เป็นไปตามโปรโตคอลที่ฉันกำหนดไว้

ฉันรู้ว่าฉันสามารถตรวจสอบได้ว่าวัตถุนั้นสอดคล้องกับโปรโตคอลหรือไม่จากนั้นเรียกใช้เมธอดอย่างปลอดภัย:

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

อย่างไรก็ตาม XCode แสดงคำเตือน:

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

อะไรคือวิธีที่ถูกต้องในการป้องกันคำเตือนนี้ ฉันไม่สามารถแคสต์self.myViewControllerเป็นMyProtocolชั้นเรียนได้

คำตอบ:


172

วิธีที่ถูกต้องในการดำเนินการคือ:

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

UIViewController <MyProtocol> *ประเภทหล่อแปลว่า "VC เป็นวัตถุ UIViewController ที่สอดคล้องกับ MyProtocol" ในขณะที่ใช้id <MyProtocol>แปลว่า "VC เป็นวัตถุของการเรียนที่ไม่รู้จักที่สอดคล้องกับ MyProtocol เป็น"

วิธีนี้คอมไพเลอร์จะให้การตรวจสอบประเภทที่ถูกต้องvc- คอมไพเลอร์จะแจ้งเตือนคุณก็ต่อเมื่อวิธีการใด ๆ ที่ไม่ได้ประกาศUIViewControllerหรือ<MyProtocol>ถูกเรียกใช้ idควรใช้ในสถานการณ์หากคุณไม่ทราบคลาส / ประเภทของวัตถุที่กำลังร่าย


2
เมื่อใช้โปรโตคอลคุณไม่ควรสนใจประเภทออบเจ็กต์จุดรวมของโปรโตคอลคือประเภทวัตถุใด ๆ สามารถนำมาใช้และใช้ได้โดยไม่ต้องส่งไปยังวัตถุเฉพาะ ดังนั้นผมจะแนะนำให้ใช้คำตอบจากที่ใดก็ได้ @andy คุณกำลังหล่อโปรโตคอลแทนการดังกล่าวข้างต้นที่ - id<MyProtocol> p = (id<MyProtocol>)self.myViewController;คำตอบนี้และ @andys มีทั้งที่ถูกต้อง แต่ของเขามากขึ้นที่ถูกต้อง
memmons

2
@Answerbot ความคิดเห็นของคุณไม่ถูกต้องและพลาดประเด็นที่ฉันให้ไว้ในย่อหน้าสุดท้ายของคำตอบ คุณอาจสนใจประเภทวัตถุหรือไม่ก็ได้ขึ้นอยู่กับสถานการณ์ จะเกิดอะไรขึ้นถ้าคุณต้องการที่จะส่งข้อความประกาศUIViewControllerไปvcในตัวอย่างในคำตอบของฉันและมันก็ประกาศเป็นid <MyProtocol>?
Nick Forge

ไม่แน่ใจว่าความคิดเห็นของฉันไม่ถูกต้องหรือไม่ ไม่ว่าในกรณีใดหากคุณกำลังตรวจสอบว่าวัตถุเป็นไปตามโปรโตคอลหรือไม่เหตุใดคุณจึงเรียกวิธีการอื่นที่ไม่เกี่ยวข้องกับโปรโตคอล ฉันจำไม่ได้ว่าจำเป็นต้องทำสิ่งนี้หรือเห็นสิ่งนี้ในรหัสที่ฉันได้ตรวจสอบแล้ว ดูเหมือนจะเป็นกลิ่นรหัสสำหรับฉัน
memmons

เพียงเพราะคุณไม่ได้เห็น / ใช้มันไม่ได้หมายความว่ามันเป็นกลิ่นรหัส นี่คือข้อมูลโค้ดที่แสดงตัวอย่างหนึ่งของการทิ้งข้อมูลประเภทโดยใช้idเป็นปัญหา: gist.github.com/nsforge/7743616
Nick Forge

60

คุณสามารถส่งได้ดังนี้:

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

สิ่งนี้ทำให้ฉันรู้สึกแย่เหมือนกัน ใน Objective-C โปรโตคอลไม่ใช่ประเภทของตัวเองดังนั้นคุณต้องระบุid(หรือประเภทอื่น ๆ เช่นNSObject) พร้อมกับโปรโตคอลที่คุณต้องการ


อาเย็นขอบคุณ ฉันเพิ่งตรวจสอบและเห็นว่าการแคสต์เป็น(id)ผลงานด้วย ฟอร์มแย่ขนาดนั้นเลยเหรอ?
Ford

1
หากคุณแคสต์เป็น id <MyProtocol> คอมไพเลอร์จะเตือนคุณหากคุณใช้วิธีการที่ไม่ได้กำหนดไว้ในโปรโตคอลนั้น
dreamlax

1
@dreamlax - นี่คือวิธีที่คอมไพเลอร์ตรวจสอบประเภทกับโปรโตคอล โปรดดูdeveloper.apple.com/documentation/Cocoa/Conceptual/ObjectiveC/…สำหรับข้อมูลเพิ่มเติม
Andy

1
@ ฟอร์ด - มันจะดีกว่าถ้าใช้โปรโตคอลโดยเฉพาะเนื่องจากวิธีนี้คอมไพเลอร์สามารถทำการตรวจสอบบางประเภทให้คุณได้
Andy

1
@ แอนดี้ฉันไม่คิดว่าคุณต้องการ '*' เนื่องจาก 'id' เป็นตัวชี้อยู่แล้ว ดังนั้น: id <MyProtocol> p = (id <MyProtocol>) self.myViewController; [p protocolMethod]; หรือเพียงแค่: [(id <MyProtocol>) self.myViewController protocolMethod];
ฟอร์ด
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.