在协议上声明静态函数有没有意义?使用该协议的客户端仍然必须在符合该协议的类型上调用函数,对吗?这破坏了不需要知道符合协议的类型的想法。有没有一种方式可以调用协议上的静态函数,而不需要知道实际符合协议的类型?
在协议上声明静态函数有没有意义?使用该协议的客户端仍然必须在符合该协议的类型上调用函数,对吗?这破坏了不需要知道符合协议的类型的想法。有没有一种方式可以调用协议上的静态函数,而不需要知道实际符合协议的类型?
好问题。以下是我的个人观点:
与在协议中声明实例方法基本相同。
是的,就像实例方法一样。
不是的。看下面的代码:
protocol Feline {
var name: String { get }
static func createRandomFeline() -> Feline
init()
}
extension Feline {
static func createRandomFeline() -> Feline {
return arc4random_uniform(2) > 0 ? Tiger() : Leopard()
}
}
class Tiger: Feline {
let name = "Tiger"
required init() {}
}
class Leopard: Feline {
let name = "Leopard"
required init() {}
}
let feline: Feline = arc4random_uniform(2) > 0 ? Tiger() : Leopard()
let anotherFeline = feline.dynamicType.createRandomFeline()
我不知道变量feline里面的真正类型是什么,我只知道它符合Feline。但是我正在调用一个静态协议方法。
我明白了,您想要调用在协议中声明的静态方法/函数,而不创建符合该协议的值。
像这样:
Feline.createRandomFeline() // DANGER: compiler is not happy now
坦白说,我不知道为什么这是不可能的。let feline: Feline ...
开头的最后两行的原因是,我真的不想或者说不需要实例化 Feline
就能调用静态函数。唯一必须这样做的理由就是找到符合该协议的底层类型。 - SirRupertIIIstatic func createRandomFeline() -> Feline
声明为static func createRandomFeline() -> Self
。现在,你明白原因了吗? :) 由于协议没有self
,因此您不能在其上调用方法。 - Sulthan是的,这是可能的:
Swift 3
protocol Thing {
static func genericFunction()
}
//... in another file
var things:[Thing] = []
for thing in things {
type(of: thing).genericFunction()
}
protocol MyProtocol {
static func aStaticFunc()
}
class SomeClassThatUsesMyProtocolButDoesntConformToIt {
var myProtocolType: MyProtocol.Type
init(protocolType: MyProtocol.Type) {
myProtocolType = protocolType
}
func aFunction() {
myProtocolType.aStaticFunc()
}
}
SomeClassThatUsesMyProtocolButDoesntConformToIt(protocolType: S.self)
来使用这个不符合协议的结构体。 - Frizlabprotocol TypeAccessible {
func type() -> Self.Type
}
extension TypeAccessible {
func type() -> Self.Type {
return Swift.type(of: self)
}
}
然后按照以下方式创建您的具体类。关键是您的协议应符合TypeAccessible
协议。
protocol FooProtocol: TypeAccessible {
static func bar()
}
class Foo: FooProtocol {
static func bar() { }
}
在呼叫现场将其用作
let instance: FooProtocol = Foo()
instance.type().bar()
如果需要更多的使用案例,只需确保您的协议符合TypeAccessible
即可。
enum PropertyScope {
case all
case none
}
struct PropertyNotifications {
static var propertyDidChange =
Notification.Name("propertyDidChangeNotification")
}
protocol Property {
typealias Scope = PropertyScope
typealias Notifications = PropertyNotifications
var scope: Scope { get set }
}
func postNotification() {
let scope: Property.Scope = .all
NotificationCenter.post(name: Property.Notifications.propertyDidChange,
object: scope)
}
这不是一个答案,而更像是对问题的扩展。假设我有以下代码:
@objc public protocol InteractivelyNameable: Nameable {
static func alertViewForNaming(completion:@escaping((_ success: Bool, _ didCancel: Bool, _ error: Error?) -> Void)) -> UIAlertController?
}
我有一个通用的视图控制器,可以管理各种类型(通用类型是.fetchableObjectType...基本上是NSFetchResult)。我需要检查特定对象类型是否符合协议,如果符合,则调用它。
类似这样:
// valid swift code
if self.dataSource.fetchableObjectType is InteractivelyNameable {
// not valid swift code
if let alert = (self.dataSource.fetchableObjectType as InteractivelyNameable).alertViewForNaming(....)
}
使用像Java接口这样的协议很少是一个好主意。它们是元类型,用于定义合同,这是完全不同的事情。
话虽如此,仅仅为了理解,我发现创建协议的静态工厂方法的等效方式最简单和有效的方法是编写一个自由函数。
它应该包含协议的名称,希望这将防止名称冲突,并提高可发现性。
在其他语言中,createP将是P的静态成员,命名为create,并被称为P.create(...),这将大大提高可发现性并保证防止名称冲突。
然而,在Swift中,对于协议来说,这不是一个选项,因此,如果协议出于某种原因真正地被用作接口的替代品,至少在函数名称中包含协议的名称是一个丑陋的解决方法,但仍然比没有好一点。
P.S. 如果目标实际上是实现类的继承层次结构,则联合样式枚举是旨在服务于此目的的工具 :)
protocol P
{
var x: Int { get }
}
func createP() -> P
{
if (todayIsMonday())
{
return A()
}
else
{
return B()
}
}
class A: P
{
var x = 5
}
class B: P
{
var x = 7
}
我遇到了这样一种情况,需要从两个不同的响应中创建相同的DomainModel
对象。所以这种(在protocol
中的静态方法帮助了我)方法对我很有帮助。
protocol BaseResponseKeyList: CodingKey {
static func getNameKey()->Self
}
enum FirstResponseKeyList: String, BaseResponseKeyList {
case name
func getNameKey()->FirstResponseKeyList {
return .name
}
}
enum SecondResponseKeyList: String, BaseResponseKeyList {
case userName
func getNameKey()->SecondResponseKeyList {
return .userName
}
}
struct MyDomainModel<T:BaseResponseKeyList> : Decodable {
var name:String?
required init(from d:Decoder) {
do {
let container = try d.container(keyedBy:T.self)
name = try container.decode(String.self, forKey:T.getNameKey())
}catch(_) {
print("error")
}
}
}
let myDomainModel = try JSONDecoder().decode(MyDomainModel <FirstResponseKeyList>.self, from: data)
let myDomainModel2 = try JSONDecoder().decode(MyDomainModel <SecondResponseKeyList>.self, from: data2)