与 Objective-C 不同,您不能在纯 Swift 中定义可选协议要求。符合协议的类型必须采用所有指定的要求。
允许可选属性要求的一种潜在方法是将它们定义为可选项,并使用仅返回 nil 的计算属性的默认实现。
protocol Nameable {
var name : String? { get }
var fullName : String? { get }
var nickname : String? { get }
}
extension Nameable {
var name : String? { return nil }
var fullName : String? { return nil }
var nickname : String? { return nil }
}
struct Person : Nameable {
// Person now has the option not to implement the protocol requirements,
// as they have default implementations that return nil
// What's cool is you can implement the optional typed property requirements with
// non-optional properties – as this doesn't break the contract with the protocol.
var name : String
}
let p = Person(name: "Allan")
print(p.name) // Allan
但是,这种方法的缺点是您可能会使用它们未实现的属性(在这种情况下为fullName 和nickName)污染符合类型的类型。
因此,如果一个类型具有这些属性在逻辑上没有意义(假设您想将 City 与 Nameable 一致——但城市(实际上)没有昵称),那么您不应该遵循它到Nameable。
如您所说,一个更灵活的解决方案是定义多个协议来定义这些要求。这样,类型就可以准确地选择他们想要实现的需求。
protocol Nameable {
var name : String { get }
}
protocol FullNameable {
var fullName : String { get }
}
protocol NickNameable {
// Even types that conform to NickNameable may have instances without nicknames.
var nickname : String? { get }
}
// A City only needs a name, not a fullname or nickname
struct City : Nameable {
var name : String
}
let london = City(name: "London")
// Person can have a name, full-name or nickname
struct Person : Nameable, FullNameable, NickNameable {
var name : String
var fullName: String
var nickname: String?
}
let allan = Person(name: "Allan", fullName: "Allan Doe", nickname: "Al")
为了方便起见,您甚至可以使用protocol composition 来定义typealias 来表示所有这三个协议,例如:
typealias CombinedNameable = Nameable & FullNameable & NickNameable
struct Person : CombinedNameable {
var name : String
var fullName: String
var nickname: String?
}