Как я могу вызвать статическую функцию в протоколе общим способом?

Есть ли смысл объявлять статическую функцию в протоколе? Клиент, использующий протокол, должен в любом случае вызывать функцию на типе, соответствующем протоколу? Это нарушает идею не знать тип, соответствующий протоколу IMO. Есть ли способ вызвать статическую функцию в протоколе таким образом, что мне не нужно знать фактический тип, соответствующий моему протоколу?

Ответ 1

Хороший вопрос. Вот моя скромная точка зрения:

Есть ли смысл объявлять статическую функцию в протоколе?

В значительной степени то же самое, что и методы экземпляра, объявленные в протоколе.

Клиент, использующий протокол, должен в любом случае вызывать функцию на типе, соответствующем протоколу?

Да, точно так же, как и функции экземпляра.

Это нарушает идею не знать тип, соответствующий протоколу 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

Честно говоря, я не знаю, почему это невозможно.

Ответ 2

да это возможно:

Swift 3

protocol Thing {
  static func genericFunction()
}

//... in another file

var things:[Thing] = []

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

Ответ 3

Спасибо @appzYourLife за помощь! Ваш ответ вдохновил мой ответ.

@appzYourLife ответил на мой вопрос. У меня была основная проблема, которую я пытался решить, и следующий код разрешает мою проблему, поэтому я отправлю это здесь, возможно, это поможет кому-то с моим одним и тем же основным вопросом:

 protocol MyProtocol {
     static func aStaticFunc()
 }

 class SomeClassThatUsesMyProtocolButDoesntConformToIt {

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

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

Ответ 4

Немного поздно на вечеринку на этом.

Здесь мое решение для "добавления" статических свойств/функций/типов к протоколу с помощью 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)

}

Ответ 5

Использование протоколов, таких как интерфейсы Java, редко является хорошей идеей. Они являются мета-типами, предназначенными для определения контрактов, что является совершенно другим видом.

Сказанное, просто для понимания, я нахожу самый простой и эффективный способ создания эквивалента статического метода factory протокола для написания свободной функции.

Он должен содержать имя протокола, надеясь, что это предотвратит столкновения имен и улучшит обнаружение.

В других языках createP будет статическим членом P, который называется create и будет вызван как P.create(...), что значительно улучшит открытость и гарантирует предотвращение конфликтов имен.

В быстрых, однако, это не вариант для протоколов, поэтому, если протоколы по какой-то причине действительно фактически используются в качестве замены интерфейсов, по крайней мере, включая имя протокола в имени функции, это уродливое обходное решение, которое все еще немного лучше чем ничего.

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

Это не ответ, так как это расширение вопроса. Скажем, у меня есть:

@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

У меня была ситуация, когда мне нужно создать один и DomainModel же объект 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)