如何以通用方式调用协议上的静态函数?

How can I call a static function on a protocol in a generic way?

在协议上声明静态函数是否有意义?使用协议的客户端必须调用符合协议的类型的函数,对吗?这打破了不必知道符合 IMO 协议的类型的想法。有没有一种方法可以在我不必知道符合我的协议的实际类型的情况下调用协议上的静态函数?

问得好。这是我的拙见:

在协议上声明静态函数是否有意义?

与在协议中声明实例方法几乎相同。

使用该协议的客户端必须在符合协议的类型上调用函数,对吗?

是的,就像实例函数一样。

这打破了不必知道符合 IMO 协议的类型的想法。

没有。看下面的代码:

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。但是我正在调用静态协议方法。

有更好的方法吗?

我明白了,您想调用在协议中声明的静态 method/function 而不创建符合协议的值。

像这样:

Feline.createRandomFeline() // DANGER: compiler is not happy now

老实说,我不知道为什么这是不可能的。

感谢@appzYourLife 的帮助!你的回答启发了我的回答。

@appzYourLife 回答了我的问题。我有一个我试图解决的潜在问题,下面的代码解决了我的问题,所以我会 post 在这里,也许它可以帮助有同样潜在问题的人:

 protocol MyProtocol {
     static func aStaticFunc()
 }

 class SomeClassThatUsesMyProtocolButDoesntConformToIt {

     var myProtocolType: MyProtocol.Type
     init(protocolType: MyProtocol.Type) {
         myProtocolType = protocolType
     }

     func aFunction() {
         myProtocolType.aStaticFunc()
     }
 }

使用 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
}

是的,这是可能的:

Swift 3

protocol Thing {
  static func genericFunction()
}

//... in another file

var things:[Thing] = []

for thing in things {
  type(of: thing).genericFunction()
}

与其说这是一个答案,不如说这是对问题的扩展。假设我有:

@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(....)
    }

参加派对有点晚了。

这是我使用 typealias.

将 "adding" 静态 properties/functions/types 协议的解决方案

例如:

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)

}

我遇到过需要根据 2 不同的响应创建相同的 DomainModel 对象的情况。所以这个(protocol 中的 static 方法帮助了我)方法帮助了我。

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)

我为这个案例创建了另一个解决方案。恕我直言,这非常干净简单。

首先,创建访问实例类型的协议。

protocol TypeAccessible {
    func type() -> Self.Type
}

extension TypeAccessible {
    func type() -> Self.Type {
        return Swift.type(of: self)
    }
}

然后像这里一样创建具体的 class。关键是你的协议应该符合 TypeAccessible 协议。

protocol FooProtocol: TypeAccessible {
    static func bar()
}

class Foo: FooProtocol {
    static func bar() { }
}

在呼叫站点上将其用作

let instance: FooProtocol = Foo()
instance.type().bar()

对于更多用例,只需确保您的协议符合 TypeAccessible 即可。