web-dev-qa-db-ja.com

一般的な方法でプロトコルの静的関数を呼び出すにはどうすればよいですか?

プロトコルで静的関数を宣言するポイントはありますか?プロトコルを使用するクライアントは、とにかくプロトコルに準拠する型で関数を呼び出す必要がありますか?これは、プロトコルIMOに準拠するタイプを知る必要がないという考えを破ります。プロトコルに準拠する実際の型を知る必要がない方法で、プロトコルの静的関数を呼び出す方法はありますか?

23
SirRupertIII

いい質問ですね。これが私の謙虚な視点です。

プロトコルで静的関数を宣言するポイントはありますか?

プロトコルでインスタンスメソッドを宣言するのとほとんど同じです。

プロトコルを使用するクライアントは、とにかくプロトコルに準拠する型で関数を呼び出す必要がありますか?

はい、インスタンス関数とまったく同じです。

これは、プロトコル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

正直なところ、これが不可能な理由はわかりません。

26
Luca Angeletti

はい、これは可能です:

Swift

protocol Thing {
  static func genericFunction()
}

//... in another file

var things:[Thing] = []

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

助けてくれてありがとう@appzYourLife!あなたの答えが私の答えに影響を与えました。

@appzYourLifeが私の質問に答えました。解決しようとしている根本的な問題があり、次のコードで問題を解決するので、ここに投稿します。同じ根本的な質問を持つ人に役立つかもしれません:

 protocol MyProtocol {
     static func aStaticFunc()
 }

 class SomeClassThatUsesMyProtocolButDoesntConformToIt {

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

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

今回のパーティーには少し遅れました。

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)

}
1
John

2異なる応答から同じDomainModelオブジェクトを作成する必要がある状況がありました。そのため、この(staticprotocolメソッドが役に立ちました)アプローチが助けになりました。

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)
0
Suryavel TR

Javaインターフェースのようなプロトコルを使用することはめったにありません。これらは完全に異なる種類のコントラクトを定義するためのメタタイプです。

そうは言っても、理解のためだけに、プロトコルの静的ファクトリメソッドに相当するものを作成して、自由な関数を記述する最も簡単で効果的な方法を見つけました。

名前の衝突を防ぎ、発見性を向上させることを期待して、プロトコルの名前を含める必要があります。

他の言語では、createPはcreateという名前のPの静的メンバーであり、P.create(...)として呼び出されます。これにより、検出可能性が大幅に向上し、名前の衝突を防ぐことができます。

ただし、Swiftでは、これはプロトコルのオプションではないため、プロトコルが何らかの理由で実際にインターフェイスの代替として実際に使用されている場合、少なくとも関数の名前にプロトコルの名前を含めることは、何もしないよりもわずかに良いい回避策です。

追伸実際に構造体を使用して継承階層のようなものを達成することが目標である場合、ユニオンスタイルの列挙型はその目的に役立つツールです:)

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
}
0
yeoman

これは質問の拡張であるため、答えではありません。私が持っていると言います:

@objc public protocol InteractivelyNameable: Nameable {

    static func alertViewForNaming(completion:@escaping((_ success: Bool, _ didCancel: Bool, _ error: Error?) -> Void)) -> UIAlertController?
}

そして、私はさまざまなタイプを管理する汎用View Controllerを持っています(一般的なタイプは.fetchableObjectType ...基本的にNSFetchResultです)。特定のオブジェクトタイプがプロトコルに準拠しているかどうかを確認し、準拠している場合はそれを呼び出します。

何かのようなもの:

    // valid Swift code
    if self.dataSource.fetchableObjectType is InteractivelyNameable {

        // not valid Swift code
        if let alert = (self.dataSource.fetchableObjectType as InteractivelyNameable).alertViewForNaming(....)
    }
0
horseshoe7