เป็นไปได้ใน Swift หรือไม่? ถ้าไม่เช่นนั้นจะมีวิธีแก้ปัญหาให้ทำหรือไม่?
เป็นไปได้ใน Swift หรือไม่? ถ้าไม่เช่นนั้นจะมีวิธีแก้ปัญหาให้ทำหรือไม่?
คำตอบ:
protocol MyProtocol {
func doSomething()
}
extension MyProtocol {
func doSomething() {
/* return a default value or just leave empty */
}
}
struct MyStruct: MyProtocol {
/* no compile error */
}
ข้อดี
ไม่มีวัตถุประสงค์เกี่ยวข้อง -C รันไทม์ (ดีไม่มีอย่างน้อยอย่างชัดเจน) ซึ่งหมายความว่าคุณสามารถปรับโครงสร้าง, enums และไม่ใช่NSObject
คลาสได้ นอกจากนี้ยังหมายความว่าคุณสามารถใช้ประโยชน์จากระบบ generics ที่ทรงพลัง
คุณสามารถมั่นใจได้ว่าจะปฏิบัติตามข้อกำหนดทั้งหมดเมื่อพบกับประเภทที่สอดคล้องกับโปรโตคอลดังกล่าว มันเป็นการใช้งานที่เป็นรูปธรรมหรือเป็นค่าเริ่มต้นเสมอ นี่คือวิธีที่ "ส่วนต่อประสาน" หรือ "สัญญา" ทำงานในภาษาอื่น
ข้อเสีย
สำหรับVoid
ความต้องการที่ไม่จำเป็นคุณจะต้องมีค่าเริ่มต้นที่สมเหตุสมผลซึ่งไม่สามารถทำได้เสมอไป อย่างไรก็ตามเมื่อคุณพบปัญหานี้หมายความว่าข้อกำหนดดังกล่าวไม่ควรมีการใช้งานจริงหรือคุณทำผิดพลาดระหว่างการออกแบบ API
คุณไม่สามารถแยกความแตกต่างระหว่างการนำไปใช้งานเริ่มต้นและไม่มีการนำไปใช้เลยอย่างน้อยที่สุดโดยไม่ต้องจัดการปัญหานั้นด้วยค่าส่งคืนพิเศษ ลองพิจารณาตัวอย่างต่อไปนี้:
protocol SomeParserDelegate {
func validate(value: Any) -> Bool
}
หากคุณให้การใช้งานเริ่มต้นซึ่งเพิ่งจะส่งกลับtrue
- มันได้อย่างรวดเร็วก่อน ตอนนี้ให้ลองใช้โค้ดหลอกต่อไปนี้:
final class SomeParser {
func parse(data: Data) -> [Any] {
if /* delegate.validate(value:) is not implemented */ {
/* parse very fast without validating */
} else {
/* parse and validate every value */
}
}
}
ไม่มีวิธีที่จะใช้การเพิ่มประสิทธิภาพเช่นนี้ - คุณไม่รู้ว่าผู้รับมอบสิทธิ์ของคุณใช้วิธีการหรือไม่
แม้ว่าจะมีหลายวิธีในการเอาชนะปัญหานี้ (โดยใช้การปิดที่เป็นตัวเลือกวัตถุผู้ร่วมประชุมที่แตกต่างกันสำหรับการดำเนินการที่แตกต่างกันเพื่อตั้งชื่อไม่กี่) ตัวอย่างนั้นแสดงปัญหาอย่างชัดเจน
@objc optional
ใช้@objc protocol MyProtocol {
@objc optional func doSomething()
}
class MyClass: NSObject, MyProtocol {
/* no compile error */
}
ข้อดี
ข้อเสีย
มันจำกัดความสามารถของโปรโตคอลของคุณอย่างรุนแรงโดยกำหนดประเภทที่สอดคล้องทั้งหมดให้เข้ากันได้กับ Objective-C ซึ่งหมายความว่าเฉพาะคลาสที่สืบทอดจากNSObject
สามารถสอดคล้องกับโปรโตคอลดังกล่าว ไม่มี structs ไม่มี enums ไม่มีประเภทที่เกี่ยวข้อง
คุณต้องตรวจสอบทุกครั้งว่ามีการใช้วิธีการเสริมหรือไม่โดยการโทรทางเลือกหรือตรวจสอบว่าประเภทที่สอดคล้องกันใช้หรือไม่ วิธีนี้อาจแนะนำให้ใช้แผ่นสำเร็จรูปจำนวนมากหากคุณเรียกวิธีการเลือกบ่อย ๆ
respondsToSelector
?
optional func doSomething(param: Int?)
ใน Swift 2 เป็นต้นไปเป็นไปได้ที่จะเพิ่มการใช้งานเริ่มต้นของโปรโตคอล สิ่งนี้จะสร้างวิธีการใหม่ของวิธีการที่เป็นตัวเลือกในโปรโตคอล
protocol MyProtocol {
func doSomethingNonOptionalMethod()
func doSomethingOptionalMethod()
}
extension MyProtocol {
func doSomethingOptionalMethod(){
// leaving this empty
}
}
ไม่ใช่วิธีที่ดีมากในการสร้างวิธีการเลือกใช้โปรโตคอล แต่ให้โอกาสคุณในการใช้ structs ในการเรียกกลับโปรโตคอล
ฉันเขียนบทสรุปเล็ก ๆ ที่นี่: https://www.avanderlee.com/swift-2-0/optional-protocol-methods/
เนื่องจากมีคำตอบบางอย่างเกี่ยวกับวิธีใช้ตัวดัดแปลงและแอตทริบิวต์ @objcเพื่อกำหนดโปรโตคอลข้อกำหนดทางเลือกฉันจะให้ตัวอย่างเกี่ยวกับวิธีใช้ส่วนขยายโปรโตคอลกำหนดโปรโตคอลตัวเลือก
โค้ดด้านล่างคือ Swift 3 *
/// Protocol has empty default implementation of the following methods making them optional to implement:
/// `cancel()`
protocol Cancelable {
/// default implementation is empty.
func cancel()
}
extension Cancelable {
func cancel() {}
}
class Plane: Cancelable {
//Since cancel() have default implementation, that is optional to class Plane
}
let plane = Plane()
plane.cancel()
// Print out *United Airlines can't cancelable*
โปรดสังเกตว่าวิธีการขยายโพรโทคอลไม่สามารถเรียกใช้โดยรหัส Objective-C และที่แย่กว่านั้นคือทีม Swift จะไม่แก้ไข https://bugs.swift.org/browse/SR-492
คำตอบอื่น ๆ ที่นี่เกี่ยวกับการทำเครื่องหมายโปรโตคอลเป็น "@objc" ไม่ทำงานเมื่อใช้ประเภทเฉพาะอย่างรวดเร็ว
struct Info {
var height: Int
var weight: Int
}
@objc protocol Health {
func isInfoHealthy(info: Info) -> Bool
}
//Error "Method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C"
เพื่อประกาศโปรโตคอลทางเลือกที่ทำงานได้ดีกับ swift ให้ประกาศฟังก์ชันเป็นตัวแปรแทนที่จะเป็น func
protocol Health {
var isInfoHealthy: (Info) -> (Bool)? { get set }
}
จากนั้นใช้โปรโตคอลดังต่อไปนี้
class Human: Health {
var isInfoHealthy: (Info) -> (Bool)? = { info in
if info.weight < 200 && info.height > 72 {
return true
}
return false
}
//Or leave out the implementation and declare it as:
//var isInfoHealthy: (Info) -> (Bool)?
}
จากนั้นคุณสามารถใช้ "?" เพื่อตรวจสอบว่ามีการใช้งานฟังก์ชั่นนี้หรือไม่
func returnEntity() -> Health {
return Human()
}
var anEntity: Health = returnEntity()
var isHealthy = anEntity.isInfoHealthy(Info(height: 75, weight: 150))?
//"isHealthy" is true
นี่คือตัวอย่างที่ชัดเจนที่มีรูปแบบการมอบหมาย
ตั้งค่าโปรโตคอลของคุณ:
@objc protocol MyProtocol:class
{
func requiredMethod()
optional func optionalMethod()
}
class MyClass: NSObject
{
weak var delegate:MyProtocol?
func callDelegate()
{
delegate?.requiredMethod()
delegate?.optionalMethod?()
}
}
ตั้งผู้รับมอบสิทธิ์เป็นคลาสและใช้โปรโตคอล ดูว่าไม่จำเป็นต้องใช้วิธีเลือกได้
class AnotherClass: NSObject, MyProtocol
{
init()
{
super.init()
let myInstance = MyClass()
myInstance.delegate = self
}
func requiredMethod()
{
}
}
สิ่งหนึ่งที่สำคัญคือวิธีการเลือกใช้เป็นทางเลือกและจำเป็นต้องมี "?" เมื่อโทร พูดถึงเครื่องหมายคำถามที่สอง
delegate?.optionalMethod?()
ในSwift 3.0
@objc protocol CounterDataSource {
@objc optional func increment(forCount count: Int) -> Int
@objc optional var fixedIncrement: Int { get }
}
มันจะช่วยประหยัดเวลาของคุณ
@objc
ที่สมาชิกทุกคนต้องการในตอนนี้?
required
ตั้งค่าสถานะ แต่มีข้อผิดพลาด: required
สามารถใช้กับการประกาศ 'init' เท่านั้น
optional
คำหลักก่อนแต่ละวิธี@objc
ไม่ใช่แค่โปรโตคอล
แนวทาง Swift ที่บริสุทธิ์พร้อมการสืบทอดโปรโตคอล:
//Required methods
protocol MyProtocol {
func foo()
}
//Optional methods
protocol MyExtendedProtocol: MyProtocol {
func bar()
}
class MyClass {
var delegate: MyProtocol
func myMethod() {
(delegate as? MyExtendedProtocol).bar()
}
}
เพื่อแสดงกลไกของคำตอบของแอนทอน:
protocol SomeProtocol {
func aMethod()
}
extension SomeProtocol {
func aMethod() {
print("extensionImplementation")
}
}
class protocolImplementingObject: SomeProtocol {
}
class protocolImplementingMethodOverridingObject: SomeProtocol {
func aMethod() {
print("classImplementation")
}
}
let noOverride = protocolImplementingObject()
let override = protocolImplementingMethodOverridingObject()
noOverride.aMethod() //prints "extensionImplementation"
override.aMethod() //prints "classImplementation"
ฉันคิดว่าก่อนถามว่าคุณจะสามารถใช้วิธีเลือกโปรโตคอลได้อย่างไรคุณควรถามว่าทำไมคุณจึงควรใช้วิธีนี้
หากเราคิดว่าโปรโตคอล swift เป็นส่วนต่อประสานในการเขียนโปรแกรมเชิงวัตถุคลาสสิกวิธีการทางเลือกที่ไม่สมเหตุสมผลและบางทีทางออกที่ดีกว่าคือการสร้างการใช้งานเริ่มต้นหรือแยกโปรโตคอลออกเป็นชุดของโปรโตคอล ระหว่างพวกเขา) เพื่อเป็นตัวแทนของการรวมกันเป็นไปได้ของวิธีการในโปรโตคอล
สำหรับการอ่านเพิ่มเติมโปรดดูhttps://useyourloaf.com/blog/swift-optional-protocol-methods/ซึ่งให้ภาพรวมที่ดีเยี่ยมในเรื่องนี้
ปิดหัวข้อเล็กน้อยจากคำถามเดิม แต่มันสร้างแนวคิดของแอนทอนออกมาและฉันคิดว่ามันอาจช่วยใครซักคน
คุณยังสามารถสร้างคุณสมบัติที่คำนวณได้ซึ่งเป็นทางเลือกสำหรับโครงสร้างที่มีส่วนขยายโปรโตคอล
คุณสามารถสร้างคุณสมบัติบนโปรโตคอลเป็นทางเลือก
protocol SomeProtocol {
var required: String { get }
var optional: String? { get }
}
นำคุณสมบัติที่คำนวณมาจากหุ่นมาใช้ในส่วนขยายโปรโตคอล
extension SomeProtocol {
var optional: String? { return nil }
}
และตอนนี้คุณสามารถใช้ structs ที่ทำหรือไม่ได้ใช้คุณสมบัติทางเลือก
struct ConformsWithoutOptional {
let required: String
}
struct ConformsWithOptional {
let required: String
let optional: String?
}
ฉันได้เขียนถึงวิธีการทำคุณสมบัติทางเลือกในโพรโทคอล Swift บนบล็อกของฉันซึ่งฉันจะคอยอัปเดตในกรณีที่สิ่งต่าง ๆ เปลี่ยนแปลงผ่านการเผยแพร่ Swift 2
วิธีการสร้างวิธีการมอบหมายและไม่จำเป็น
@objc protocol InterViewDelegate:class {
@objc optional func optfunc() // This is optional
func requiredfunc()// This is required
}
นี่เป็นตัวอย่างที่ง่ายมากสำหรับการเรียนอย่างรวดเร็วเท่านั้นไม่ใช่สำหรับโครงสร้างหรือการแจกแจง โปรดทราบว่าวิธีการโปรโตคอลเป็นตัวเลือกมีสองระดับของการผูกมัดตัวเลือกที่เล่น คลาสที่ใช้โปรโตคอลต้องการแอตทริบิวต์ @objc ในการประกาศ
@objc protocol CollectionOfDataDelegate{
optional func indexDidChange(index: Int)
}
@objc class RootView: CollectionOfDataDelegate{
var data = CollectionOfData()
init(){
data.delegate = self
data.indexIsNow()
}
func indexDidChange(index: Int) {
println("The index is currently: \(index)")
}
}
class CollectionOfData{
var index : Int?
weak var delegate : CollectionOfDataDelegate?
func indexIsNow(){
index = 23
delegate?.indexDidChange?(index!)
}
}
delegate?.indexDidChange?(index!)
?
protocol CollectionOfDataDelegate{ func indexDidChange(index: Int) }
คุณจะเรียกมันโดยไม่มีเครื่องหมายคำถาม: delegate?.indexDidChange(index!)
เมื่อคุณตั้งค่าข้อกำหนดเพิ่มเติมสำหรับวิธีในโปรโตคอลประเภทที่จะสอดคล้องกับมันอาจจะไม่ใช้วิธีนั้น ดังนั้นจึง?
ใช้เพื่อตรวจสอบการใช้งานและหากไม่มีโปรแกรมจะไม่ทำงานผิดพลาด @Unheilig
weak var delegate : CollectionOfDataDelegate?
(ตรวจสอบการอ้างอิงที่อ่อนแอ?)
delegate?
ใช้งานให้กับคำตอบของคุณได้หรือไม่? ข้อมูลนั้นควรจะอยู่ในนั้นเพื่อผู้อื่นในอนาคต ฉันต้องการโหวตขึ้น แต่ข้อมูลนั้นควรอยู่ในคำตอบจริงๆ
ถ้าคุณต้องการที่จะทำใน swift อย่างบริสุทธิ์วิธีที่ดีที่สุดคือการจัดเตรียมการใช้งานโดยปริยายหากคุณส่งคืน Swift type เช่นเช่น structกับประเภท Swift
ตัวอย่าง:
struct magicDatas {
var damagePoints : Int?
var manaPoints : Int?
}
protocol magicCastDelegate {
func castFire() -> magicDatas
func castIce() -> magicDatas
}
extension magicCastDelegate {
func castFire() -> magicDatas {
return magicDatas()
}
func castIce() -> magicDatas {
return magicDatas()
}
}
จากนั้นคุณสามารถใช้โปรโตคอลโดยไม่ต้องกำหนดทุกfunc
มีสองวิธีที่คุณสามารถสร้างวิธีเลือกในโปรโตคอล swift
1 - ตัวเลือกแรกคือการทำเครื่องหมายโปรโตคอลของคุณโดยใช้แอตทริบิวต์ @objc แม้ว่านี่จะหมายความว่าสามารถนำไปใช้กับคลาสเท่านั้น แต่หมายความว่าคุณทำเครื่องหมายแต่ละวิธีว่าเป็นตัวเลือกเช่นนี้:
@objc protocol MyProtocol {
@objc optional func optionalMethod()
}
2 - วิธีที่รวดเร็วกว่า: ตัวเลือกนี้ดีกว่า เขียนการใช้งานเริ่มต้นของวิธีการทางเลือกที่ไม่ทำอะไรเช่นนี้
protocol MyProtocol {
func optionalMethod()
func notOptionalMethod()
}
extension MyProtocol {
func optionalMethod() {
//this is a empty implementation to allow this method to be optional
}
}
Swift มีคุณสมบัติที่เรียกว่าส่วนขยายที่ช่วยให้เราสามารถให้การใช้งานเริ่มต้นสำหรับวิธีการที่เราต้องการเป็นทางเลือก
ทางเลือกหนึ่งคือการเก็บไว้เป็นตัวแปรฟังก์ชั่นเสริม:
struct MyAwesomeStruct {
var myWonderfulFunction : Optional<(Int) -> Int> = nil
}
let squareCalculator =
MyAwesomeStruct(myWonderfulFunction: { input in return input * input })
let thisShouldBeFour = squareCalculator.myWonderfulFunction!(2)
กำหนดฟังก์ชั่นในโปรโตคอลและสร้างส่วนขยายสำหรับโปรโตคอลนั้นจากนั้นสร้างการใช้งานที่ว่างเปล่าสำหรับฟังก์ชั่นที่คุณต้องการใช้เป็นทางเลือก
ในการกำหนดOptional
Protocol
อย่างรวดเร็วคุณควรใช้@objc
คำสำคัญก่อนProtocol
ประกาศและattribute
/ method
ประกาศภายในโปรโตคอลนั้น ด้านล่างนี้เป็นตัวอย่างคุณสมบัติทางเลือกของโปรโตคอล
@objc protocol Protocol {
@objc optional var name:String?
}
class MyClass: Protocol {
// No error
}
วาง@optional
ด้านหน้าของเมธอดหรือคุณสมบัติ
@optional
ไม่ใช่แม้แต่คำหลักที่ถูกต้อง เป็นoptional
และคุณต้องประกาศคลาสและโปรโตคอลด้วย@objc
คุณลักษณะ