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

时间:2016-05-13 21:05:59

标签: swift protocols swift-protocols

是否有必要在协议上声明静态函数?使用协议的客户端必须在符合协议的类型上调用该函数吗?这打破了不必知道符合IMO协议的类型的想法。有没有办法以一种我不必知道符合我的协议的实际类型的方式调用协议上的静态函数?

8 个答案:

答案 0 :(得分:24)

好问题。这是我谦虚的观点:

是否有必要在协议上声明静态函数?

与在协议中声明的实例方法非常相似。

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

是的,与实例函数完全相同。

这打破了不必知道符合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。但是我正在调用静态协议方法。

有更好的方法吗?

我知道,您希望调用协议中声明的静态方法/函数,而不创建符合协议的值。

这样的事情:

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

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

答案 1 :(得分:4)

是的,这是可能的:

Swift 3

protocol Thing {
  static func genericFunction()
}

//... in another file

var things:[Thing] = []

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

答案 2 :(得分:2)

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

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

 protocol MyProtocol {
     static func aStaticFunc()
 }

 class SomeClassThatUsesMyProtocolButDoesntConformToIt {

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

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

答案 3 :(得分:1)

在这个派对上有点晚了。

这是我的解决方案"添加"使用typealias的协议的静态属性/函数/类型。

例如:

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)

}

答案 4 :(得分:1)

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

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

protocol 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即可。

答案 5 :(得分:0)

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

答案 6 :(得分:0)

这不是答案,而是问题的延伸。说我有:

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

答案 7 :(得分:0)

我遇到一种情况,我需要根据DomainModel个不同的响应来创建相同的2对象。因此,这种static中的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)