Есть ли смысл объявлять статическую функцию в протоколе? Клиент, использующий протокол, должен в любом случае вызывать функцию на типе, соответствующем протоколу? Это нарушает идею не знать тип, соответствующий протоколу 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)