【发布时间】:2014-07-24 19:50:53
【问题描述】:
在 Swift 中可以吗?如果没有,那么有解决方法吗?
【问题讨论】:
标签: ios swift optional-parameters swift-protocols swift-extensions
在 Swift 中可以吗?如果没有,那么有解决方法吗?
【问题讨论】:
标签: ios swift optional-parameters swift-protocols swift-extensions
protocol MyProtocol {
func doSomething()
}
extension MyProtocol {
func doSomething() {
/* return a default value or just leave empty */
}
}
struct MyStruct: MyProtocol {
/* no compile error */
}
优势
不涉及 Objective-C 运行时(嗯,至少不明确)。这意味着您可以使结构、枚举和非NSObject 类符合它。此外,这意味着您可以利用强大的泛型系统。
在遇到符合此类协议的类型时,您始终可以确保满足所有要求。它始终是具体实现或默认实现。这就是“接口”或“合同”在其他语言中的行为方式。
缺点
对于非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 的类才能符合此类协议。没有结构,没有枚举,没有关联的类型。
您必须始终检查是否实现了可选方法,方法是可选地调用或检查符合的类型是否实现了它。如果您经常调用可选方法,这可能会引入大量样板。
【讨论】:
在 Swift 2 及更高版本中,可以添加协议的默认实现。这在协议中创建了一种新的可选方法。
protocol MyProtocol {
func doSomethingNonOptionalMethod()
func doSomethingOptionalMethod()
}
extension MyProtocol {
func doSomethingOptionalMethod(){
// leaving this empty
}
}
这不是创建可选协议方法的好方法,但可以让您在协议回调中使用结构体。
我在这里写了一个小总结: https://www.avanderlee.com/swift-2-0/optional-protocol-methods/
【讨论】:
这是委托模式的具体示例。
设置您的协议:
@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?()
【讨论】:
由于有一些关于如何使用可选修饰符和@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”的其他答案在使用特定于 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"
为了声明与 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
【讨论】:
在 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 协议中创建可选方法。
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 有一个称为扩展的功能,它允许我们为那些我们希望成为可选的方法提供默认实现。
【讨论】:
我认为在问如何你可以实现一个可选的协议方法之前,你应该问为什么你应该实现一个。
如果我们将 swift 协议视为经典面向对象编程中的接口,可选方法没有多大意义,也许更好的解决方案是创建默认实现,或者将协议分离为一组协议(它们之间可能有一些继承关系)来表示协议中可能的方法组合。
如需进一步阅读,请参阅https://useyourloaf.com/blog/swift-optional-protocol-methods/,它对此事提供了很好的概述。
【讨论】:
与最初的问题略有偏离,但它建立在 Antoine 的想法之上,我认为它可能会对某人有所帮助。
您还可以将计算属性设置为具有协议扩展的结构的可选属性。
您可以将协议上的属性设为可选
protocol SomeProtocol {
var required: String { get }
var optional: String? { get }
}
在协议扩展中实现虚拟计算属性
extension SomeProtocol {
var optional: String? { return nil }
}
现在您可以使用实现或不实现可选属性的结构
struct ConformsWithoutOptional {
let required: String
}
struct ConformsWithOptional {
let required: String
let optional: String?
}
我还写了optional properties in Swift protocols on my blog 的操作方法,如果 Swift 2 版本发生变化,我会不断更新。
【讨论】:
如何创建可选和必需的委托方法。
@objc protocol InterViewDelegate:class {
@objc optional func optfunc() // This is optional
func requiredfunc()// This is required
}
【讨论】:
这是一个非常简单的示例,仅适用于 swift 类,不适用于结构或枚举。 请注意,协议方法是可选的,有两个级别的可选链接在起作用。 采用该协议的类也需要在其声明中添加@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 类型(例如带有 Swift 类型的 struct)时提供默认的具体实现
示例:
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
的情况下实现协议【讨论】:
一种选择是将它们存储为可选函数变量:
struct MyAwesomeStruct {
var myWonderfulFunction : Optional<(Int) -> Int> = nil
}
let squareCalculator =
MyAwesomeStruct(myWonderfulFunction: { input in return input * input })
let thisShouldBeFour = squareCalculator.myWonderfulFunction!(2)
【讨论】:
我们先来了解一下区别
第一个例子——如果你写UITableViewDataSource那么你需要强制写两个方法——这是默认协议的快捷方式
第二个例子 - 如果你写UITableViewDelegate 并且知道它没有显示红色错误,请添加所有委托方法。这取决于您要使用哪种方法。我们可以叫optional method!
让我们通过一个例子来理解这一点
First Swift Way 默认协议方法
class ContactModel{
var firstname: String?
var lastname: String?
}
protocol ContactDataSource: AnyObject{
func contactConfiguration(contact: ContactModel)
}
class ViewController: ContactDataSource{
func contactConfiguration(contact: ContactModel) {
print(contact)
}
}
第二种方法 - 可选协议
@objc
class UserModel: NSObject{
var firstname: String = ""
}
@objc protocol UserDataSource{
func contactConfiguration(user: UserModel)
@objc optional func userInfo(user: UserModel)
}
class ViewController: UserDataSource{
func contactConfiguration(user: UserModel) {
print(user)
}
}
注意:如果您可以在可选协议中看到,我还没有编写 userInfo 方法,所以由您决定。这意味着有和没有向类添加方法 它工作正常。 - 作为协议中的可选方法调用
你必须声明类和协议 @objc 属性,它只适用于 Class 而不是 struct!
第三种方法 - 使用扩展的可选协议
注意:你可以使用 Struct 或 Class
class UserModel{
var firstname: String = ""
}
或
struct UserModel{
var firstname: String = ""
}
与
protocol UserDataSource{
func contactConfiguration(user: UserModel)
}
extension UserDataSource{
func userInfo(user: UserModel){}
}
class myview: UserDataSource{
func contactConfiguration(user: UserModel) {
print(user)
}
}
【讨论】:
在协议中定义函数并为该协议创建扩展,然后为要用作可选的函数创建空实现。
【讨论】:
要在swift中定义OptionalProtocol,你应该在Protocol声明之前使用@objc关键字,并在该协议中使用attribute/method声明。
下面是一个协议的可选属性示例。
@objc protocol Protocol {
@objc optional var name:String?
}
class MyClass: Protocol {
// No error
}
【讨论】:
把@optional放在方法或属性前面。
【讨论】:
@optional 甚至不是正确的关键字。它是optional,你必须用@objc属性声明类和协议。