Saya akhirnya memotivasi diri saya untuk memperbarui jawaban saya, Anda dapat mempertimbangkan kembali menerimanya.
akashivskyy
@akashivskyy Saya menerima jawaban Anda karena lebih baik menampilkan semua opsi yang tersedia dan pro dan kontra mereka.
Selvin
Jawaban:
506
1. Menggunakan implementasi standar (lebih disukai).
protocolMyProtocol{func doSomething()}extensionMyProtocol{func doSomething(){/* return a default value or just leave empty */}}structMyStruct:MyProtocol{/* no compile error */}
Keuntungan
Tidak ada runtime Objective-C yang terlibat (well, setidaknya tidak secara eksplisit). Ini berarti Anda dapat menyesuaikan struct, enum dan non- NSObjectkelas untuk itu. Juga, ini berarti Anda dapat memanfaatkan sistem obat generik yang kuat.
Anda selalu dapat memastikan bahwa semua persyaratan dipenuhi ketika menemukan tipe yang sesuai dengan protokol tersebut. Itu selalu merupakan implementasi konkret atau standar. Ini adalah bagaimana "antarmuka" atau "kontrak" berperilaku dalam bahasa lain.
Kekurangan
Untuk non- Voidpersyaratan, Anda harus memiliki nilai default yang masuk akal , yang tidak selalu memungkinkan. Namun, ketika Anda menghadapi masalah ini, itu berarti bahwa persyaratan seperti itu harus benar-benar tidak memiliki implementasi standar, atau bahwa Anda membuat kesalahan selama desain API.
Anda tidak dapat membedakan antara implementasi default dan tidak ada implementasi sama sekali , setidaknya tanpa mengatasi masalah itu dengan nilai pengembalian khusus. Perhatikan contoh berikut:
Jika Anda memberikan implementasi default yang baru saja kembali true- baik-baik saja pada pandangan pertama. Sekarang, pertimbangkan kode pseudo berikut:
finalclassSomeParser{func parse(data:Data)->[Any]{if/* delegate.validate(value:) is not implemented */{/* parse very fast without validating */}else{/* parse and validate every value */}}}
Tidak ada cara untuk mengimplementasikan optimasi seperti itu - Anda tidak bisa tahu apakah delegasi Anda menerapkan metode atau tidak.
Meskipun ada sejumlah cara berbeda untuk mengatasi masalah ini (menggunakan penutupan opsional, objek delegasi yang berbeda untuk beberapa operasi yang berbeda untuk menyebutkan beberapa), contoh itu menyajikan masalah dengan jelas.
2. Menggunakan @objc optional.
@objc protocolMyProtocol{@objc optionalfunc doSomething()}classMyClass:NSObject,MyProtocol{/* no compile error */}
Keuntungan
Tidak diperlukan implementasi standar. Anda hanya mendeklarasikan metode opsional atau variabel dan Anda siap untuk pergi.
Kekurangan
Ini sangat membatasi kemampuan protokol Anda dengan mengharuskan semua jenis yang sesuai agar kompatibel dengan Objective-C. Ini berarti, hanya kelas-kelas yang diwarisi dari yang NSObjectdapat memenuhi protokol tersebut. Tidak ada struct, tidak ada enum, tidak ada tipe yang terkait.
Anda harus selalu memeriksa apakah metode opsional diterapkan dengan memanggil atau memeriksa secara opsional apakah tipe yang sesuai mengimplementasikannya. Ini mungkin memperkenalkan banyak pelat baja jika Anda sering memanggil metode opsional.
Lihatlah cara peningkatan metode opsional di swift menggunakan ekstensi (di bawah)
Daniel Kanaan
1
Dan bagaimana satu tes untuk mendukung metode protokol opsional dalam sebuah instance? respondsToSelector?
devios1
3
Jangan lakukan ini! Swift tidak mendukung metode opsional dalam protokol karena suatu alasan.
fpg1503
2
Metode ini tidak mendukung parameter opsional. Yaitu Anda tidak bisa melakukan ituoptional func doSomething(param: Int?)
SoftDesigner
4
Tentunya jawaban ini pada dasarnya salah saat ini, bertahun-tahun kemudian. Hari ini di Swift Anda cukup menambahkan ekstensi untuk implementasi default, itu adalah aspek dasar dari Swift. (Seperti yang ditunjukkan oleh semua jawaban modern di bawah ini.) Adalah salah jika menambahkan bendera objc, saat ini.
Fattie
394
Di Swift 2 dan selanjutnya, mungkin untuk menambahkan implementasi protokol standar. Ini menciptakan cara baru metode opsional dalam protokol.
protocolMyProtocol{func doSomethingNonOptionalMethod()func doSomethingOptionalMethod()}extensionMyProtocol{func doSomethingOptionalMethod(){// leaving this empty
}}
Ini bukan cara yang sangat bagus dalam membuat metode protokol opsional, tetapi memberi Anda kemungkinan untuk menggunakan struct di dalam panggilan balik protokol.
Ini mungkin cara paling bersih untuk melakukannya di Swift. Sayang sekali itu tidak berfungsi sebelum Swift 2.0.
Entalpi
13
@MattQuiros Saya menemukan bahwa Anda memang perlu mendeklarasikan fungsi dalam definisi protokol, jika tidak, fungsi ekstensi no-op tidak akan ditimpa di kelas Anda yang sesuai dengan protokol.
Ian Pearce
3
@IanPearce benar, dan ini tampaknya dirancang. Dalam pembicaraan "Pemrograman Berorientasi Protokol" (408) di WWDC, mereka berbicara tentang metode dalam protokol utama yang menjadi "titik penyesuaian" yang ditawarkan untuk jenis yang sesuai. Titik penyesuaian yang diperlukan tidak menerima definisi dalam ekstensi; titik opsional tidak. Metode pada protokol yang umumnya tidak boleh dikustomisasi sepenuhnya dinyatakan / ditentukan dalam ekstensi untuk melarang tipe yang sesuai untuk dikustomisasi kecuali Anda secara spesifik dilemparkan ke dynamicType untuk menunjukkan Anda menginginkan implementasi kustom konformer.
matthias
4
@ FrankrankYu Anda dapat melakukan ini, tetapi kemudian Anda mencemari desain API Anda dengan 'lemparan' di mana sebenarnya tidak diperlukan. Saya lebih suka ide "protokol mikro". Misalnya setiap metode mengkonfirmasi ke satu protokol dan kemudian Anda dapat memeriksa: jika objek adalah protokol
Darko
3
@Antoine Saya pikir Anda harus membuat fungsi di ekstensi publik, karena fungsi-fungsi dalam protokol bersifat publik menurut definisi. Solusi Anda tidak akan berfungsi ketika protokol akan digunakan di luar modulnya.
Vadim Eisenberg
39
Karena ada beberapa jawaban tentang cara menggunakan pengubah opsional dan atribut @objc untuk mendefinisikan protokol persyaratan opsional, saya akan memberikan sampel tentang cara menggunakan ekstensi protokol untuk menentukan protokol opsional.
Kode di bawah ini adalah Swift 3. *.
/// Protocol has empty default implementation of the following methods making them optional to implement:
/// `cancel()`
protocolCancelable{/// default implementation is empty.
func cancel()}extensionCancelable{func cancel(){}}classPlane:Cancelable{//Since cancel() have default implementation, that is optional to class Plane
}let plane =Plane()
plane.cancel()//Print out *UnitedAirlines can't cancelable*
Harap perhatikan metode ekstensi protokol tidak dapat dipanggil oleh kode Objective-C, dan lebih buruk lagi adalah tim Swift tidak akan memperbaikinya. https://bugs.swift.org/browse/SR-492
Kerja bagus! Ini harus menjadi jawaban yang benar karena ini memecahkan masalah tanpa melibatkan runtime Objective-C.
Lukas
benar-benar bagus!
tania_S
dari dokumentasi cepat - "Persyaratan protokol dengan implementasi standar yang disediakan oleh ekstensi berbeda dari persyaratan protokol opsional. Meskipun tipe yang sesuai tidak harus menyediakan implementasi mereka sendiri, persyaratan dengan implementasi standar dapat dipanggil tanpa rantai opsional."
Mec Os
34
Jawaban lain di sini yang melibatkan menandai protokol sebagai "@objc" tidak berfungsi saat menggunakan tipe swift-specific.
structInfo{var height:Intvar weight:Int}@objc protocolHealth{func isInfoHealthy(info:Info)->Bool}//Error"Method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C"
Untuk mendeklarasikan protokol opsional yang bekerja dengan baik dengan cepat, deklarasikan fungsi sebagai variabel, bukan fungsi.
Dan kemudian implementasikan protokolnya sebagai berikut
classHuman:Health{var isInfoHealthy:(Info)->(Bool)?={ info inif info.weight <200&& info.height >72{returntrue}returnfalse}//Or leave out the implementation and declare it as:
//var isInfoHealthy: (Info) -> (Bool)?
}
Anda kemudian dapat menggunakan "?" untuk memeriksa apakah fungsi telah diterapkan atau tidak
Hanya kelas, protokol, metode, dan properti yang dapat menggunakan @objc. Jika Anda menggunakan parameter Enum dalam definisi metode protokol @ objc, Anda akan menemui ajal.
khunshan
1
@ Khunshan, metode ini tidak memerlukan apa pun yang ditandai @ objc, apa yang Anda maksud?
Zag
ini merupakan informasi tentang topik bahwa enum tidak dapat digunakan di antara swift dan objc yang untuk pernyataan lainnya dapat dijembatani dengan kata kunci @objc.
khunshan
34
Ini adalah contoh nyata dengan pola pendelegasian.
Saya pikir sebelum bertanya bagaimana Anda dapat mengimplementasikan metode protokol opsional, Anda harus bertanya mengapa Anda harus menerapkannya.
Jika kita menganggap protokol cepat sebagai Antarmuka dalam pemrograman berorientasi objek klasik, metode opsional tidak masuk akal, dan mungkin solusi yang lebih baik adalah dengan membuat implementasi standar, atau memisahkan protokol menjadi satu set protokol (mungkin dengan beberapa hubungan warisan di antara mereka) untuk mewakili kemungkinan kombinasi metode dalam protokol.
Berikut adalah contoh yang sangat sederhana untuk Kelas swift SAJA, dan bukan untuk struktur atau enumerasi. Perhatikan bahwa metode protokol bersifat opsional, memiliki dua level rantai opsional yang dimainkan. Juga kelas yang mengadopsi protokol membutuhkan atribut @objc dalam deklarasi.
@objc protocolCollectionOfDataDelegate{optionalfunc indexDidChange(index:Int)}@objc classRootView:CollectionOfDataDelegate{var data =CollectionOfData()init(){
data.delegate =self
data.indexIsNow()}func indexDidChange(index:Int){
println("The index is currently: \(index)")}}classCollectionOfData{var index :Int?weakvar delegate :CollectionOfDataDelegate?func indexIsNow(){
index =23
delegate?.indexDidChange?(index!)}}
Dapatkah Anda menjelaskan sedikit lebih dalam " dua tingkat opsional bermain " bagian, yaitu ini: delegate?.indexDidChange?(index!)?
Unheilig
2
Jika kami telah menulis protokol untuk memiliki metode non opsional seperti ini: protocol CollectionOfDataDelegate{ func indexDidChange(index: Int) } maka Anda akan memanggilnya tanpa tanda tanya: delegate?.indexDidChange(index!) Ketika Anda menetapkan persyaratan opsional untuk metode dalam protokol, Tipe yang akan sesuai dengannya mungkin TIDAK menerapkan metode itu , jadi ?digunakan untuk memeriksa implementasi dan Jika tidak ada program tidak akan crash. @Unheilig
Blessing Lopes
weak var delegate : CollectionOfDataDelegate?(pastikan referensi lemah?)
Alfie Hanssen
@BlessingLopes Bisakah Anda menambahkan penjelasan tentang delegate?penggunaan ke jawaban Anda? Informasi itu harus benar-benar menjadi milik orang lain di masa depan. Saya ingin mengungguli ini, tetapi informasi itu harus benar-benar ada dalam jawabannya.
Johnathon Sullinger
3
jika Anda ingin melakukannya dengan swift murni, cara terbaik adalah dengan memberikan partikel implementasi standar jika Anda mengembalikan tipe Swift seperti misalnya struct dengan tipe Swift
Ada dua cara Anda dapat membuat metode opsional dalam protokol cepat.
1 - Opsi pertama adalah menandai protokol Anda menggunakan atribut @objc. Meskipun ini berarti hanya dapat diadopsi oleh kelas, itu berarti Anda menandai metode individual sebagai opsional seperti ini:
2 - Cara yang lebih cepat: Opsi ini lebih baik. Tulis implementasi default dari metode opsional yang tidak melakukan apa-apa, seperti ini.
protocolMyProtocol{func optionalMethod()func notOptionalMethod()}extensionMyProtocol{func optionalMethod(){//this is a empty implementation to allow this method to be optional
}}
Swift memiliki ekstensi fitur yang disebut yang memungkinkan kami untuk memberikan implementasi default untuk metode yang kami inginkan opsional.
Tetapkan fungsi dalam protokol dan buat ekstensi untuk protokol itu, lalu buat implementasi kosong untuk fungsi yang ingin Anda gunakan sebagai opsional.
Untuk mendefinisikan OptionalProtocoldengan cepat Anda harus menggunakan @objckata kunci sebelum Protocoldeklarasi dan attribute/ methoddeklarasi di dalam protokol itu. Di bawah ini adalah contoh Properti Opsional dari protokol.
@objc protocolProtocol{@objc optionalvar name:String?}classMyClass:Protocol{// No error
}
Meskipun ini dapat menjawab pertanyaan, lebih baik menambahkan deskripsi tentang bagaimana jawaban ini dapat membantu menyelesaikan masalah. Silakan baca Bagaimana saya menulis jawaban yang bagus untuk mengetahui lebih banyak.
Jawaban:
1. Menggunakan implementasi standar (lebih disukai).
Keuntungan
Tidak ada runtime Objective-C yang terlibat (well, setidaknya tidak secara eksplisit). Ini berarti Anda dapat menyesuaikan struct, enum dan non-
NSObject
kelas untuk itu. Juga, ini berarti Anda dapat memanfaatkan sistem obat generik yang kuat.Anda selalu dapat memastikan bahwa semua persyaratan dipenuhi ketika menemukan tipe yang sesuai dengan protokol tersebut. Itu selalu merupakan implementasi konkret atau standar. Ini adalah bagaimana "antarmuka" atau "kontrak" berperilaku dalam bahasa lain.
Kekurangan
Untuk non-
Void
persyaratan, Anda harus memiliki nilai default yang masuk akal , yang tidak selalu memungkinkan. Namun, ketika Anda menghadapi masalah ini, itu berarti bahwa persyaratan seperti itu harus benar-benar tidak memiliki implementasi standar, atau bahwa Anda membuat kesalahan selama desain API.Anda tidak dapat membedakan antara implementasi default dan tidak ada implementasi sama sekali , setidaknya tanpa mengatasi masalah itu dengan nilai pengembalian khusus. Perhatikan contoh berikut:
Jika Anda memberikan implementasi default yang baru saja kembali
true
- baik-baik saja pada pandangan pertama. Sekarang, pertimbangkan kode pseudo berikut:Tidak ada cara untuk mengimplementasikan optimasi seperti itu - Anda tidak bisa tahu apakah delegasi Anda menerapkan metode atau tidak.
Meskipun ada sejumlah cara berbeda untuk mengatasi masalah ini (menggunakan penutupan opsional, objek delegasi yang berbeda untuk beberapa operasi yang berbeda untuk menyebutkan beberapa), contoh itu menyajikan masalah dengan jelas.
2. Menggunakan
@objc optional
.Keuntungan
Kekurangan
Ini sangat membatasi kemampuan protokol Anda dengan mengharuskan semua jenis yang sesuai agar kompatibel dengan Objective-C. Ini berarti, hanya kelas-kelas yang diwarisi dari yang
NSObject
dapat memenuhi protokol tersebut. Tidak ada struct, tidak ada enum, tidak ada tipe yang terkait.Anda harus selalu memeriksa apakah metode opsional diterapkan dengan memanggil atau memeriksa secara opsional apakah tipe yang sesuai mengimplementasikannya. Ini mungkin memperkenalkan banyak pelat baja jika Anda sering memanggil metode opsional.
sumber
respondsToSelector
?optional func doSomething(param: Int?)
Di Swift 2 dan selanjutnya, mungkin untuk menambahkan implementasi protokol standar. Ini menciptakan cara baru metode opsional dalam protokol.
Ini bukan cara yang sangat bagus dalam membuat metode protokol opsional, tetapi memberi Anda kemungkinan untuk menggunakan struct di dalam panggilan balik protokol.
Saya menulis ringkasan kecil di sini: https://www.avanderlee.com/swift-2-0/optional-protocol-methods/
sumber
Karena ada beberapa jawaban tentang cara menggunakan pengubah opsional dan atribut @objc untuk mendefinisikan protokol persyaratan opsional, saya akan memberikan sampel tentang cara menggunakan ekstensi protokol untuk menentukan protokol opsional.
Kode di bawah ini adalah Swift 3. *.
Harap perhatikan metode ekstensi protokol tidak dapat dipanggil oleh kode Objective-C, dan lebih buruk lagi adalah tim Swift tidak akan memperbaikinya. https://bugs.swift.org/browse/SR-492
sumber
Jawaban lain di sini yang melibatkan menandai protokol sebagai "@objc" tidak berfungsi saat menggunakan tipe swift-specific.
Untuk mendeklarasikan protokol opsional yang bekerja dengan baik dengan cepat, deklarasikan fungsi sebagai variabel, bukan fungsi.
Dan kemudian implementasikan protokolnya sebagai berikut
Anda kemudian dapat menggunakan "?" untuk memeriksa apakah fungsi telah diterapkan atau tidak
sumber
Ini adalah contoh nyata dengan pola pendelegasian.
Siapkan Protokol Anda:
Atur delegasi ke kelas dan mengimplementasikan Protokol. Pastikan metode opsional tidak perlu diterapkan.
Satu hal penting adalah bahwa metode opsional adalah opsional dan memerlukan "?" saat menelepon. Sebutkan tanda tanya kedua.
sumber
Di Swift 3.0
Ini akan menghemat waktu Anda.
sumber
@objc
diperlukan pada semua anggota sekarang?required
flag, tetapi dengan kesalahan:required
hanya dapat digunakan pada deklarasi 'init'.optional
kata kunci sebelum setiap metode.sumber
@objc
, bukan hanya protokol.Pendekatan Swift murni dengan pewarisan protokol:
sumber
Untuk mengilustrasikan mekanisme jawaban Antoine:
sumber
Saya pikir sebelum bertanya bagaimana Anda dapat mengimplementasikan metode protokol opsional, Anda harus bertanya mengapa Anda harus menerapkannya.
Jika kita menganggap protokol cepat sebagai Antarmuka dalam pemrograman berorientasi objek klasik, metode opsional tidak masuk akal, dan mungkin solusi yang lebih baik adalah dengan membuat implementasi standar, atau memisahkan protokol menjadi satu set protokol (mungkin dengan beberapa hubungan warisan di antara mereka) untuk mewakili kemungkinan kombinasi metode dalam protokol.
Untuk bacaan lebih lanjut, lihat https://useyourloaf.com/blog/swift-optional-protocol-methods/ , yang memberikan gambaran yang sangat baik tentang masalah ini.
sumber
Agak keluar dari topik dari pertanyaan awal, tetapi itu membangun ide Antoine dan saya pikir itu mungkin membantu seseorang.
Anda juga dapat membuat properti yang dihitung opsional untuk struct dengan ekstensi protokol.
Anda dapat membuat properti pada protokol opsional
Menerapkan properti yang dihitung dummy dalam ekstensi protokol
Dan sekarang Anda dapat menggunakan struct yang memiliki atau tidak memiliki properti opsional diimplementasikan
Saya juga menulis cara melakukan properti opsional dalam protokol Swift di blog saya , yang saya akan terus perbarui jika terjadi perubahan melalui rilis Swift 2.
sumber
Cara membuat metode delegasi opsional dan wajib.
sumber
Berikut adalah contoh yang sangat sederhana untuk Kelas swift SAJA, dan bukan untuk struktur atau enumerasi. Perhatikan bahwa metode protokol bersifat opsional, memiliki dua level rantai opsional yang dimainkan. Juga kelas yang mengadopsi protokol membutuhkan atribut @objc dalam deklarasi.
sumber
delegate?.indexDidChange?(index!)
?protocol CollectionOfDataDelegate{ func indexDidChange(index: Int) }
maka Anda akan memanggilnya tanpa tanda tanya:delegate?.indexDidChange(index!)
Ketika Anda menetapkan persyaratan opsional untuk metode dalam protokol, Tipe yang akan sesuai dengannya mungkin TIDAK menerapkan metode itu , jadi?
digunakan untuk memeriksa implementasi dan Jika tidak ada program tidak akan crash. @Unheiligweak var delegate : CollectionOfDataDelegate?
(pastikan referensi lemah?)delegate?
penggunaan ke jawaban Anda? Informasi itu harus benar-benar menjadi milik orang lain di masa depan. Saya ingin mengungguli ini, tetapi informasi itu harus benar-benar ada dalam jawabannya.jika Anda ingin melakukannya dengan swift murni, cara terbaik adalah dengan memberikan partikel implementasi standar jika Anda mengembalikan tipe Swift seperti misalnya struct dengan tipe Swift
contoh:
maka Anda dapat mengimplementasikan protokol tanpa mendefinisikan setiap fungsi
sumber
Ada dua cara Anda dapat membuat metode opsional dalam protokol cepat.
1 - Opsi pertama adalah menandai protokol Anda menggunakan atribut @objc. Meskipun ini berarti hanya dapat diadopsi oleh kelas, itu berarti Anda menandai metode individual sebagai opsional seperti ini:
2 - Cara yang lebih cepat: Opsi ini lebih baik. Tulis implementasi default dari metode opsional yang tidak melakukan apa-apa, seperti ini.
Swift memiliki ekstensi fitur yang disebut yang memungkinkan kami untuk memberikan implementasi default untuk metode yang kami inginkan opsional.
sumber
Salah satu opsi adalah menyimpannya sebagai variabel fungsi opsional:
sumber
Tetapkan fungsi dalam protokol dan buat ekstensi untuk protokol itu, lalu buat implementasi kosong untuk fungsi yang ingin Anda gunakan sebagai opsional.
sumber
Untuk mendefinisikan
Optional
Protocol
dengan cepat Anda harus menggunakan@objc
kata kunci sebelumProtocol
deklarasi danattribute
/method
deklarasi di dalam protokol itu. Di bawah ini adalah contoh Properti Opsional dari protokol.sumber
Letakkan
@optional
di depan metode atau properti.sumber
@optional
bahkan bukan kata kunci yang benar. Ituoptional
, dan Anda harus mendeklarasikan kelas dan protokol dengan@objc
atributnya.