Có thể có trong Swift? Nếu không thì có một cách giải quyết để làm điều đó?
Có thể có trong Swift? Nếu không thì có một cách giải quyết để làm điều đó?
Câu trả lời:
protocol MyProtocol {
func doSomething()
}
extension MyProtocol {
func doSomething() {
/* return a default value or just leave empty */
}
}
struct MyStruct: MyProtocol {
/* no compile error */
}
Ưu điểm
Không có thời gian chạy Objective-C có liên quan (tốt, ít nhất là không rõ ràng). Điều này có nghĩa là bạn có thể tuân thủ các cấu trúc, enum và các NSObject
lớp không theo nó. Ngoài ra, điều này có nghĩa là bạn có thể tận dụng hệ thống tướng mạnh mẽ.
Bạn luôn có thể chắc chắn rằng tất cả các yêu cầu được đáp ứng khi gặp các loại phù hợp với giao thức đó. Nó luôn luôn là triển khai cụ thể hoặc mặc định. Đây là cách "giao diện" hoặc "hợp đồng" hoạt động trong các ngôn ngữ khác.
Nhược điểm
Dành cho người khôngVoid
yêu cầu yêu cầu, bạn cần phải có một giá trị mặc định hợp lý , điều này không phải lúc nào cũng có thể. Tuy nhiên, khi bạn gặp phải vấn đề này, điều đó có nghĩa là yêu cầu đó thực sự không có triển khai mặc định hoặc bạn đã mắc lỗi trong quá trình thiết kế API.
Bạn không thể phân biệt giữa thực hiện mặc định và không thực hiện gì cả , ít nhất là không giải quyết vấn đề đó bằng các giá trị trả về đặc biệt. Hãy xem xét ví dụ sau:
protocol SomeParserDelegate {
func validate(value: Any) -> Bool
}
Nếu bạn cung cấp một triển khai mặc định mà chỉ trả về true
- nó sẽ ổn ngay từ cái nhìn đầu tiên. Bây giờ, hãy xem xét mã giả sau đây:
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 */
}
}
}
Không có cách nào để thực hiện tối ưu hóa như vậy - bạn không thể biết liệu đại biểu của mình có thực hiện phương pháp hay không.
Mặc dù có một số cách khác nhau để khắc phục vấn đề này (sử dụng các bao đóng tùy chọn, các đối tượng ủy nhiệm khác nhau cho các hoạt động khác nhau để đặt tên cho một số), ví dụ đó trình bày rõ vấn đề.
@objc optional
.@objc protocol MyProtocol {
@objc optional func doSomething()
}
class MyClass: NSObject, MyProtocol {
/* no compile error */
}
Ưu điểm
Nhược điểm
Nó hạn chế nghiêm trọng khả năng của giao thức của bạn bằng cách yêu cầu tất cả các loại tuân thủ phải tương thích với Objective-C. Điều này có nghĩa, chỉ các lớp kế thừa từ NSObject
có thể phù hợp với giao thức đó. Không cấu trúc, không enums, không có loại liên quan.
Bạn phải luôn kiểm tra xem một phương thức tùy chọn có được thực hiện hay không bằng cách gọi hoặc kiểm tra tùy chọn nếu loại tuân thủ thực hiện nó. Điều này có thể giới thiệu rất nhiều mẫu soạn sẵn nếu bạn thường xuyên gọi các phương thức tùy chọn.
respondsToSelector
?
optional func doSomething(param: Int?)
Trong Swift 2 trở đi, có thể thêm các cài đặt mặc định của giao thức. Điều này tạo ra một cách mới của các phương thức tùy chọn trong các giao thức.
protocol MyProtocol {
func doSomethingNonOptionalMethod()
func doSomethingOptionalMethod()
}
extension MyProtocol {
func doSomethingOptionalMethod(){
// leaving this empty
}
}
Đây không phải là một cách thực sự hay trong việc tạo các phương thức giao thức tùy chọn, nhưng cung cấp cho bạn khả năng sử dụng các cấu trúc trong các cuộc gọi lại giao thức.
Tôi đã viết một bản tóm tắt nhỏ ở đây: https://www.avanderlee.com/swift-2-0/optional-protatio-methods/
Vì có một số câu trả lời về cách sử dụng công cụ sửa đổi tùy chọn và thuộc tính @objc để xác định giao thức yêu cầu tùy chọn, tôi sẽ đưa ra một ví dụ về cách sử dụng tiện ích mở rộng giao thức xác định giao thức tùy chọn.
Mã bên dưới là 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*
Vui lòng lưu ý các phương thức mở rộng giao thức không thể được gọi bằng mã Objective-C và tệ hơn là nhóm Swift sẽ không sửa nó. https://bugs.swift.org/browse/SR-492
Các câu trả lời khác ở đây liên quan đến việc đánh dấu giao thức là "@objc" không hoạt động khi sử dụng các loại dành riêng cho swift.
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"
Để khai báo các giao thức tùy chọn hoạt động tốt với swift, hãy khai báo các hàm dưới dạng các biến thay vì của func.
protocol Health {
var isInfoHealthy: (Info) -> (Bool)? { get set }
}
Và sau đó thực hiện giao thức như sau
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)?
}
Sau đó bạn có thể sử dụng "?" để kiểm tra xem chức năng đã được thực hiện hay chưa
func returnEntity() -> Health {
return Human()
}
var anEntity: Health = returnEntity()
var isHealthy = anEntity.isInfoHealthy(Info(height: 75, weight: 150))?
//"isHealthy" is true
Dưới đây là một ví dụ cụ thể với mô hình đoàn.
Thiết lập giao thức của bạn:
@objc protocol MyProtocol:class
{
func requiredMethod()
optional func optionalMethod()
}
class MyClass: NSObject
{
weak var delegate:MyProtocol?
func callDelegate()
{
delegate?.requiredMethod()
delegate?.optionalMethod?()
}
}
Đặt đại biểu cho một lớp và thực hiện Giao thức. Xem rằng phương pháp tùy chọn không cần phải được thực hiện.
class AnotherClass: NSObject, MyProtocol
{
init()
{
super.init()
let myInstance = MyClass()
myInstance.delegate = self
}
func requiredMethod()
{
}
}
Một điều quan trọng là phương thức tùy chọn là tùy chọn và cần "?" khi gọi. Đề cập đến dấu hỏi thứ hai.
delegate?.optionalMethod?()
Trong Swift 3.0
@objc protocol CounterDataSource {
@objc optional func increment(forCount count: Int) -> Int
@objc optional var fixedIncrement: Int { get }
}
Nó sẽ tiết kiệm thời gian của bạn.
@objc
cần thiết cho tất cả các thành viên bây giờ?
required
cờ, nhưng có lỗi: required
chỉ có thể được sử dụng trên các khai báo 'init'.
optional
từ khóa trước mỗi phương pháp.@objc
, không chỉ là giao thức.
Một cách tiếp cận Swift thuần túy với kế thừa giao thức:
//Required methods
protocol MyProtocol {
func foo()
}
//Optional methods
protocol MyExtendedProtocol: MyProtocol {
func bar()
}
class MyClass {
var delegate: MyProtocol
func myMethod() {
(delegate as? MyExtendedProtocol).bar()
}
}
Để minh họa các cơ chế trả lời của Antoine:
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"
Tôi nghĩ rằng trước khi hỏi làm thế nào bạn có thể thực hiện một phương thức giao thức tùy chọn, bạn nên hỏi tại sao bạn nên thực hiện một phương thức.
Nếu chúng ta nghĩ về các giao thức nhanh như một Giao diện trong lập trình hướng đối tượng cổ điển, các phương thức tùy chọn không có nhiều ý nghĩa và có lẽ một giải pháp tốt hơn là tạo ra triển khai mặc định hoặc tách giao thức thành một bộ giao thức (có thể có một số quan hệ thừa kế giữa chúng) để thể hiện sự kết hợp có thể của các phương thức trong giao thức.
Để đọc thêm, hãy xem https://useyourloaf.com/blog/swift-optional-protatio-methods/ , trong đó cung cấp một cái nhìn tổng quan tuyệt vời về vấn đề này.
Hơi lạc đề khỏi câu hỏi ban đầu, nhưng nó xây dựng ý tưởng của Antoine và tôi nghĩ nó có thể giúp được ai đó.
Bạn cũng có thể làm cho các thuộc tính được tính tùy chọn cho các cấu trúc có phần mở rộng giao thức.
Bạn có thể tạo một thuộc tính trên giao thức tùy chọn
protocol SomeProtocol {
var required: String { get }
var optional: String? { get }
}
Thực hiện thuộc tính giả trong phần mở rộng giao thức
extension SomeProtocol {
var optional: String? { return nil }
}
Và bây giờ bạn có thể sử dụng các cấu trúc có hoặc không có thuộc tính tùy chọn được triển khai
struct ConformsWithoutOptional {
let required: String
}
struct ConformsWithOptional {
let required: String
let optional: String?
}
Tôi cũng đã viết cách thực hiện các thuộc tính tùy chọn trong các giao thức Swift trên blog của mình , điều này tôi sẽ cập nhật trong trường hợp mọi thứ thay đổi thông qua các bản phát hành Swift 2.
Làm thế nào để tạo các phương thức đại biểu tùy chọn và bắt buộc.
@objc protocol InterViewDelegate:class {
@objc optional func optfunc() // This is optional
func requiredfunc()// This is required
}
Đây là một ví dụ rất đơn giản cho các lớp nhanh chóng CHỈ và không dành cho các cấu trúc hoặc bảng liệt kê. Lưu ý rằng phương thức giao thức là tùy chọn, có hai cấp độ chuỗi tùy chọn khi chơi. Ngoài ra lớp chấp nhận giao thức cần thuộc tính @objc trong khai báo của nó.
@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) }
thì bạn sẽ gọi nó mà không có dấu hỏi: delegate?.indexDidChange(index!)
Khi bạn đặt một yêu cầu tùy chọn cho một phương thức trong một giao thức, Loại sẽ tuân thủ theo phương thức đó có thể KHÔNG thực hiện phương thức đó , do đó, ?
được sử dụng để kiểm tra việc thực hiện và Nếu không có chương trình sẽ không bị sập. @Unheilig
weak var delegate : CollectionOfDataDelegate?
(đảm bảo tham chiếu yếu?)
delegate?
cách sử dụng vào câu trả lời của mình không? Thông tin đó nên thực sự thuộc về những người khác trong tương lai. Tôi muốn nâng cao điều này, nhưng thông tin đó thực sự nên có trong câu trả lời.
nếu bạn muốn làm điều đó trong swift thuần túy, cách tốt nhất là cung cấp một chi tiết triển khai mặc định nếu bạn trả về một kiểu Swift như ví dụ struct với các kiểu Swift
thí dụ :
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()
}
}
sau đó bạn có thể thực hiện giao thức mà không cần định nghĩa mỗi func
Có hai cách bạn có thể tạo phương thức tùy chọn trong giao thức swift.
1 - Tùy chọn đầu tiên là đánh dấu giao thức của bạn bằng thuộc tính @objc. Mặc dù điều này có nghĩa là nó chỉ có thể được chấp nhận bởi các lớp, nhưng điều đó có nghĩa là bạn đánh dấu các phương thức riêng lẻ là tùy chọn như thế này:
@objc protocol MyProtocol {
@objc optional func optionalMethod()
}
2 - Một cách nhanh hơn: Tùy chọn này là tốt hơn. Viết các triển khai mặc định của các phương thức tùy chọn không làm gì cả, như thế này.
protocol MyProtocol {
func optionalMethod()
func notOptionalMethod()
}
extension MyProtocol {
func optionalMethod() {
//this is a empty implementation to allow this method to be optional
}
}
Swift có một tính năng gọi là tiện ích mở rộng cho phép chúng tôi cung cấp triển khai mặc định cho các phương thức mà chúng tôi muốn là tùy chọn.
Một tùy chọn là lưu trữ chúng dưới dạng các biến chức năng tùy chọn:
struct MyAwesomeStruct {
var myWonderfulFunction : Optional<(Int) -> Int> = nil
}
let squareCalculator =
MyAwesomeStruct(myWonderfulFunction: { input in return input * input })
let thisShouldBeFour = squareCalculator.myWonderfulFunction!(2)
Xác định chức năng trong giao thức và tạo phần mở rộng cho giao thức đó, sau đó tạo triển khai trống cho chức năng mà bạn muốn sử dụng làm tùy chọn.
Để xác định Optional
Protocol
trong swift, bạn nên sử dụng @objc
từ khóa trước khi Protocol
khai báo và attribute
/ method
khai báo bên trong giao thức đó. Dưới đây là một mẫu thuộc tính tùy chọn của một giao thức.
@objc protocol Protocol {
@objc optional var name:String?
}
class MyClass: Protocol {
// No error
}
Đặt @optional
trước các phương thức hoặc thuộc tính.
@optional
thậm chí không phải là từ khóa chính xác. Đó là optional
, và bạn phải khai báo lớp và giao thức với @objc
thuộc tính.