it-swarm-id.com

Mendefinisikan kategori untuk protokol di Objective-C?

Di Objective-C, saya bisa menambahkan metode ke kelas yang ada dengan kategori, mis.

@interface NSString (MyCategory)
- (BOOL) startsWith: (NSString*) prefix;
@end

Apakah mungkin untuk melakukan ini dengan protokol, mis. Jika ada protokol NSString, sesuatu seperti:

@interface <NSString> (MyCategory)
- (BOOL) startsWith: (NSString*) prefix;
@end

Saya ingin melakukan ini karena saya memiliki beberapa ekstensi untuk NSObject (kelas), hanya menggunakan metode NSObject publik, dan saya ingin ekstensi itu juga berfungsi dengan objek yang mengimplementasikan protokol.

Untuk memberikan contoh lebih lanjut, bagaimana jika saya ingin menulis metode logDescription yang mencetak deskripsi objek ke log:

- (void) logDescription {
    NSLog(@"%@", [self description]);
}

Tentu saja saya dapat menambahkan metode ini ke NSObject, tetapi ada kelas lain yang tidak mewarisi dari NSObject, di mana saya juga ingin memiliki metode ini, mis. NSProxy. Karena metode ini hanya menggunakan anggota publik protokol, akan lebih baik untuk menambahkannya ke protokol.

Sunting: Java 8 sekarang memiliki ini dengan "metode ekstensi virtual" di antarmuka: http://cr.openjdk.Java.net/~briangoetz/lambda/Defender%20Methods%20v4.pdf . Inilah yang ingin saya lakukan di Objective-C. Saya tidak melihat pertanyaan ini mendapatkan banyak perhatian ...

Salam, Jochen

37
Jochen

Jawaban singkat: Tidak.

Jawaban panjang: bagaimana cara kerjanya? Bayangkan Anda dapat menambahkan metode ke protokol yang ada? Bagaimana cara kerjanya? Bayangkan kami ingin menambahkan metode lain ke NSCoding, katakanlah -(NSArray *) codingKeys; Metode ini adalah metode yang diperlukan yang mengembalikan array kunci yang digunakan untuk menyandikan objek.

Masalahnya adalah bahwa ada kelas yang ada (seperti, katakanlah NSString) yang sudah mengimplementasikan NSCoding, tetapi jangan mengimplementasikan metode codingKeys kami. Apa yang harus terjadi Bagaimana kerangka kerja pra-kompilasi tahu apa yang harus dilakukan ketika pesan wajib ini dikirim ke kelas yang tidak mengimplementasikannya?

Anda dapat mengatakan "kami dapat menambahkan definisi metode ini melalui kategori" atau "kami dapat mengatakan bahwa metode apa pun yang ditambahkan melalui kategori protokol ini secara eksplisit opsional". Ya, Anda bisa melakukan ini dan secara teoritis mengatasi masalah yang saya jelaskan di atas. Tetapi jika Anda akan melakukannya, Anda mungkin hanya membuatnya sebagai kategori di tempat pertama, dan kemudian periksa untuk memastikan kelas respondsToSelector: sebelum memanggil metode.

25
Dave DeLong

Meskipun benar bahwa Anda tidak dapat menentukan kategori untuk protokol (dan tidak ingin, karena Anda tidak tahu apa-apa tentang objek yang ada), Anda dapat mendefinisikan kategori sedemikian rupa sehingga kode hanya berlaku untuk objek dari tipe yang diberikan yang memiliki protokol yang diinginkan (semacam spesialisasi templat parsial seperti C++).

Penggunaan utama untuk sesuatu seperti ini adalah ketika Anda ingin mendefinisikan kategori yang bergantung pada versi kelas yang dikustomisasi. (Bayangkan saya memiliki subkelas UIViewController yang sesuai dengan protokol Foo, artinya mereka memiliki properti foo, kode kategori saya mungkin memerlukan properti foo, tetapi saya tidak dapat menerapkannya pada protokol Foo, dan jika saya menerapkannya saja ke UIViewController, kode tidak akan dikompilasi secara default, dan memaksanya mengkompilasi berarti seseorang melakukan introspeksi, atau hanya mengacaukan, mungkin memanggil kode Anda yang tergantung pada protokol. Pendekatan hybrid dapat bekerja seperti ini:

@protocol Foo
- (void)fooMethod

@property (retain) NSString *foo;
@end

@implementation UIViewController (FooCategory)

- (void)fooMethod {
    if (![self conformsToProtocol:@protocol(Foo)]) {
        return;
    }

    UIViewController<Foo> *me = (UIViewController<Foo>*) self;
    // For the rest of the method, use "me" instead of "self"
    NSLog(@"My foo property is \"%@\"", me.foo);
}
@end

Dengan pendekatan hybrid, Anda dapat menulis kode hanya sekali (per kelas yang seharusnya mengimplementasikan protokol) dan pastikan bahwa itu tidak akan memengaruhi instance kelas yang tidak sesuai dengan protokol.

Kelemahannya adalah bahwa sintesis/definisi properti masih harus terjadi di masing-masing subkelas.

22
Douglas Mayle

Ini tidak benar-benar bermakna untuk dilakukan karena protokol tidak dapat benar-benar mengimplementasikan metode ini. Protokol adalah cara menyatakan bahwa Anda mendukung beberapa metode. Menambahkan metode ke daftar ini di luar protokol berarti bahwa semua "menyesuaikan" kelas secara tidak sengaja mendeklarasikan metode baru meskipun mereka tidak mengimplementasikannya. Jika beberapa kelas mengimplementasikan protokol NSObject tetapi tidak turun dari NSObject, dan kemudian Anda menambahkan metode ke protokol, itu akan merusak kesesuaian kelas.

Namun, Anda dapat membuat protokol baru yang menyertakan protokol lama dengan deklarasi seperti @protocol SpecialObject <NSObject>.

7
Chuck

Adam Sharp memposting solusi yang bekerja untuk saya.

Ini melibatkan 3 langkah:

  1. Menentukan metode yang ingin Anda tambahkan sebagai @optional pada protokol.
  2. Membuat objek yang ingin diperluas sesuai dengan protokol itu.
  3. Menyalin metode-metode tersebut ke objek-objek tersebut pada saat runtime.

Lihat tautan untuk detail selengkapnya.

0
Senseful

jika Anda sudah menulis kategori, mengapa tidak menambahkan definisi protokol di header tepat setelah definisi kategori?

yaitu.

@interface NSString (MyCategory)
- (BOOL) startsWith: (NSString*) prefix;
@end

@protocol MyExtendedProtocolName <NSString>
//Method declarations go here
@end

dengan cara ini setiap kelas yang mengimpor header kategori juga akan mendapatkan definisi protokol, dan Anda dapat menambahkannya ke kelas Anda ..

@interface MyClass <OriginalProtocol,MyExtendedProtocolName>

juga, berhati-hatilah saat mensubclass NSString, ini adalah cluster dan Anda mungkin tidak selalu mendapatkan perilaku yang Anda harapkan.

0
pxl

Saya pikir Anda mungkin mencampuradukkan istilah di sana-sini. Ekstensi, Kategori, Protokol, Antarmuka, dan Kelas adalah semua hal yang berbeda di Objective-C. Dalam Bahasa Objective-C 2.0 Apple menjelaskan perbedaan dengan sangat baik, termasuk manfaat dan kelemahan menggunakan kategori dan ekstensi.

Jika Anda memikirkannya, apa itu "Kategori" atau "Perpanjangan" dalam pengertian konseptual? Ini adalah cara menambahkan fungsionalitas ke Kelas. Di Objective-C, protokol dirancang untuk tidak memiliki implementasi. Oleh karena itu, bagaimana Anda menambahkan atau memperluas implementasi dari sesuatu yang tidak memiliki implementasi untuk memulai?

0
slf