Di Swift saya dapat secara eksplisit mengatur tipe variabel dengan mendeklarasikannya sebagai berikut:
var object: TYPE_NAME
Jika kita ingin melangkah lebih jauh dan mendeklarasikan variabel yang sesuai dengan beberapa protokol, kita dapat menggunakan protocol
deklaratif:
var object: protocol<ProtocolOne,ProtocolTwo>//etc
Bagaimana jika saya ingin mendeklarasikan objek yang sesuai dengan satu atau lebih protokol dan juga dari tipe kelas dasar tertentu? Persamaan Objective-C akan terlihat seperti ini:
NSSomething<ABCProtocolOne,ABCProtocolTwo> * object = ...;
Di Swift saya berharap akan terlihat seperti ini:
var object: TYPE_NAME,ProtocolOne//etc
Ini memberi kami fleksibilitas untuk dapat menangani implementasi tipe dasar serta antarmuka tambahan yang ditentukan dalam protokol.
Apakah ada cara lain yang lebih jelas yang mungkin saya lewatkan?
Contoh
Sebagai contoh, katakanlah saya memiliki UITableViewCell
pabrik yang bertanggung jawab untuk mengembalikan sel yang sesuai dengan protokol. Kita dapat dengan mudah mengatur fungsi generik yang mengembalikan sel yang sesuai dengan protokol:
class CellFactory {
class func createCellForItem<T: UITableViewCell where T:MyProtocol >(item: SpecialItem,tableView: UITableView) -> T {
//etc
}
}
nanti saya ingin menghapus sel-sel ini sambil memanfaatkan jenis dan protokolnya
var cell: MyProtocol = CellFactory.createCellForItem(somethingAtIndexPath) as UITableViewCell
Ini mengembalikan kesalahan karena sel tampilan tabel tidak sesuai dengan protokol ...
Saya ingin dapat menentukan bahwa sel adalah a UITableViewCell
dan sesuai dengan MyProtocol
deklarasi variabel?
Pembenaran
Jika Anda sudah familiar dengan Factory Pattern, ini akan masuk akal dalam konteks untuk mengembalikan objek dari kelas tertentu yang mengimplementasikan antarmuka tertentu.
Sama seperti dalam contoh saya, terkadang kami ingin mendefinisikan antarmuka yang masuk akal saat diterapkan ke objek tertentu. Contoh saya dari sel tampilan tabel adalah salah satu justifikasi tersebut.
Sementara tipe yang disediakan tidak sepenuhnya sesuai dengan antarmuka yang disebutkan, objek yang dikembalikan pabrik melakukannya dan saya ingin fleksibilitas dalam berinteraksi dengan tipe kelas dasar dan antarmuka protokol yang dinyatakan
sumber
NSSomething<ABCProtocolOne,ABCProtocolTwo> * object = ...;
. Objek ini sepertinya tidak berguna karenaNSSomething
sudah tahu apa yang sesuai dengannya. Jika tidak sesuai dengan salah satu protokol di<>
Anda akan mengalamiunrecognised selector ...
crash. Ini tidak memberikan keamanan tipe sama sekali.Jawaban:
Di Swift 4 sekarang dimungkinkan untuk mendeklarasikan variabel yang merupakan subclass dari suatu tipe dan mengimplementasikan satu atau lebih protokol pada saat yang bersamaan.
Untuk melakukan variabel opsional:
atau sebagai parameter metode:
Apple mengumumkan ini di WWDC 2017 di Sesi 402: Yang baru di Swift
sumber
Anda tidak dapat mendeklarasikan variabel seperti
atau mendeklarasikan tipe pengembalian fungsi seperti
Anda dapat mendeklarasikan sebagai parameter fungsi seperti ini, tetapi pada dasarnya ini adalah up-casting.
Untuk saat ini, yang dapat Anda lakukan hanyalah seperti:
Dengan ini, secara teknis
cell
identik denganasProtocol
.Tetapi, untuk compiler, hanya
cell
memiliki interfaceUITableViewCell
, sedangkanasProtocol
interface hanya protokol. Jadi, ketika Anda ingin memanggilUITableViewCell
metode, Anda harus menggunakancell
variabel. Jika Anda ingin memanggil metode protokol, gunakanasProtocol
variabel.Jika Anda yakin bahwa sel sesuai dengan protokol, Anda tidak perlu menggunakan
if let ... as? ... {}
. Suka:sumber
-> UITableViewCell<MyProtocol>
, ini tidak valid, karenaUITableViewCell
bukan tipe generik. Saya pikir ini bahkan tidak dapat dikompilasi.cell
hanya memiliki metode protokol (untuk kompilator).Sayangnya, Swift tidak mendukung kesesuaian protokol tingkat objek. Namun, ada solusi yang agak canggung yang dapat memenuhi tujuan Anda.
Kemudian, di mana pun Anda perlu melakukan apa pun yang dimiliki UIViewController, Anda akan mengakses aspek .viewController dari struct dan apa pun yang Anda butuhkan dari aspek protokol, Anda akan mereferensikan .protocol.
Misalnya:
Sekarang kapan pun Anda membutuhkan mySpecialViewController untuk melakukan apa pun yang terkait dengan UIViewController, Anda cukup mereferensikan mySpecialViewController.viewController dan kapan pun Anda membutuhkannya untuk melakukan beberapa fungsi protokol, Anda mereferensikan mySpecialViewController.protocol.
Mudah-mudahan Swift 4 akan memungkinkan kita untuk mendeklarasikan objek dengan protokol yang menyertainya di masa mendatang. Tapi untuk saat ini, ini berhasil.
Semoga ini membantu!
sumber
Mungkin saya salah, tetapi apakah Anda tidak berbicara tentang menambahkan kesesuaian protokol ke
UITableCellView
kelas? Protokol dalam hal ini diperluas ke kelas dasar, dan bukan objeknya. Lihat dokumentasi Apple tentang Deklarasi Adopsi Protokol dengan Ekstensi yang dalam kasus Anda akan seperti ini:Selain dokumentasi Swift yang telah direferensikan, lihat juga artikel Nate Cooks Fungsi generik untuk tipe yang tidak kompatibel dengan contoh lebih lanjut.
Protokol Adopsi hanya akan melakukan ini, membuat objek mematuhi protokol yang diberikan. Sadarilah sisi negatifnya, bahwa variabel dari jenis protokol tertentu tidak mengetahui apa pun di luar protokol. Tetapi ini dapat dielakkan dengan mendefinisikan protokol yang memiliki semua metode / variabel / ...
Jika Anda menginginkan metode umum, variabel untuk menyesuaikan dengan protokol dan tipe kelas dasar, Anda mungkin kurang beruntung. Tapi sepertinya Anda perlu mendefinisikan protokol cukup luas untuk memiliki metode kesesuaian yang diperlukan, dan pada saat yang sama cukup sempit untuk memiliki opsi untuk mengadopsi ke kelas dasar tanpa terlalu banyak pekerjaan (yaitu hanya menyatakan bahwa kelas sesuai dengan protokol).
sumber
Saya pernah mengalami situasi serupa ketika mencoba menautkan koneksi interaktor generik saya di Storyboards (IB tidak akan mengizinkan Anda untuk menghubungkan outlet ke protokol, hanya contoh objek), yang saya hadapi hanya dengan menutupi ivar publik kelas dasar dengan komputasi pribadi Properti. Meskipun hal ini tidak mencegah seseorang untuk membuat penugasan ilegal, hal ini memberikan cara yang nyaman untuk mencegah interaksi yang tidak diinginkan dengan aman dengan instance yang tidak sesuai pada waktu proses. (yaitu mencegah pemanggilan metode delegasi ke objek yang tidak sesuai dengan protokol.)
Contoh:
"OutputReceiver" dideklarasikan opsional, begitu juga dengan "protocolOutputReceiver" pribadi. Dengan selalu mengakses outputReceiver (alias delegate) melalui yang terakhir (properti yang dihitung), saya secara efektif memfilter objek apa pun yang tidak sesuai dengan protokol. Sekarang saya cukup menggunakan rangkaian opsional untuk dengan aman memanggil objek delegasi apakah itu mengimplementasikan protokol atau bahkan ada.
Untuk menerapkan ini pada situasi Anda, Anda dapat memiliki ivar publik bertipe "YourBaseClass?" (sebagai lawan dari AnyObject), dan menggunakan properti komputasi privat untuk menegakkan kesesuaian protokol. FWIW.
sumber