Transmisikan instance kelas ke @protocol di Objective-C

102

Saya memiliki objek (UIViewController) yang mungkin atau mungkin tidak sesuai dengan protokol yang saya tetapkan.

Saya tahu saya dapat menentukan apakah objek tersebut sesuai dengan protokol, kemudian dengan aman memanggil metode tersebut:

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

Namun, XCode menunjukkan peringatan:

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

Apa cara yang benar untuk mencegah peringatan ini? Sepertinya saya tidak bisa berperan self.myViewControllersebagai MyProtocolkelas.

Mengarungi
sumber

Jawaban:

171

Cara yang benar untuk melakukannya adalah dengan melakukan:

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

The UIViewController <MyProtocol> *jenis-cast diterjemahkan menjadi "vc adalah objek UIViewController yang paling sesuai untuk MyProtocol", sedangkan dengan menggunakan id <MyProtocol>diterjemahkan menjadi "vc adalah obyek dari kelas yang tidak diketahui yang paling sesuai untuk MyProtocol".

Dengan cara ini kompilator akan memberi Anda pemeriksaan jenis yang tepat vc- kompilator hanya akan memberi Anda peringatan jika ada metode yang tidak dideklarasikan UIViewControlleratau <MyProtocol>dipanggil. idsebaiknya hanya digunakan dalam situasi tersebut jika Anda tidak mengetahui kelas / jenis objek yang dilemparkan.

Nick Forge
sumber
2
Saat menggunakan protokol, Anda seharusnya tidak peduli dengan jenis objeknya - inti dari protokol adalah bahwa semua jenis objek dapat mengadopsinya dan digunakan tanpa harus mentransmisikan ke objek tertentu. Jadi, saya akan merekomendasikan menggunakan jawaban oleh @andy di mana pun Anda mentransmisikan ke protokol daripada di atas - id<MyProtocol> p = (id<MyProtocol>)self.myViewController;Jawaban ini dan @andys keduanya benar, tetapi jawabannya lebih benar.
memmons
2
@Answerbot komentar Anda salah, dan meleset dari poin yang saya buat di paragraf terakhir jawaban saya. Anda mungkin peduli atau tidak peduli tentang jenis objek, itu tergantung pada situasinya. Apa yang terjadi jika Anda ingin mengirim pesan yang dideklarasikan UIViewControllerke vcdalam contoh di jawaban saya, dan itu dinyatakan sebagai id <MyProtocol>?
Nick Forge
Tidak yakin apa yang membuat komentar saya salah? Bagaimanapun, jika Anda memeriksa apakah suatu objek sesuai dengan protokol, mengapa Anda kemudian memanggil beberapa metode lain yang tidak terkait dengan protokol? Saya tidak ingat pernah perlu melakukan ini atau melihat ini dalam kode yang telah saya ulas. Sepertinya bau kode bagi saya.
memmons
Hanya karena Anda belum pernah melihat / menggunakannya, bukan berarti itu berbau kode. Berikut potongan kode yang menunjukkan satu contoh di mana membuang informasi jenis dengan menggunakan idmerupakan masalah: gist.github.com/nsforge/7743616
Nick Forge
60

Anda dapat mentransmisikannya seperti ini:

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

Hal ini membuat saya sedikit terkejut juga. Di Objective-C, protokol bukanlah tipe itu sendiri, jadi Anda perlu menentukan id(atau tipe lain, seperti NSObject) bersama dengan protokol yang Anda inginkan.

Andy
sumber
Ah, keren, terima kasih. Saya baru saja memeriksa dan melihat bahwa casting itu (id)berfungsi juga. Apakah itu bentuk yang buruk?
Ford
1
Jika Anda mentransmisikannya sebagai id <MyProtocol> maka kompilator akan memperingatkan Anda jika Anda menggunakan metode yang tidak ditentukan dalam protokol itu.
dreamlax
1
@dreamlax - Ini adalah cara kompilator melakukan pemeriksaan tipe terhadap protokol. Lihat developer.apple.com/documentation/Cocoa/Conceptual/ObjectiveC/… untuk info lebih lanjut.
Andy
1
@Ford - akan lebih baik untuk menggunakan protokol secara khusus, karena dengan cara itu kompilator dapat melakukan beberapa pemeriksaan tipe untuk Anda.
Andy
1
@Andy, saya rasa Anda tidak memerlukan '*' karena 'id' sudah menjadi penunjuk. Jadi: id <MyProtocol> p = (id <MyProtocol>) self.myViewController; [p protocolMethod]; Atau hanya: [(id <MyProtocol>) self.myViewController protocolMethod];
Ford