web-dev-qa-db-ja.com

Swift)のパラメータとしてプロトコルを渡す方法

Objective-Cでは、protocolをパラメーターとして渡す方法を知っています。

- (void)MyMethod:(Protocol *)myparameter

しかし、Swiftには、これ以上Protocol型はありません。

どちらであるかを知らずに、プロトコルをパラメーターとして渡すにはどうすればよいですか?

17
Jean Lebrument

あなたのコメントの1つであなたは言います:

「目的のプロトコルを実装するクラスの型の配列を返すメソッドを作成したい。」

次のようなことを試しましたか?

//notice the use of @objc here
@objc protocol AlertProtocol
{
    func getMyName()->String
}

class Class1 : AlertProtocol
{
     let name = "Object 1"
     func getMyName() -> String
    {
        return name
    }
}

class Class2 : AlertProtocol
{
    let name = "Object 2"
    func getMyName() -> String
    {
        return name
    }
}

//borrowing from and refactoring siLo's answer
func classesConformingToProtocol(proto:Protocol) -> [AnyClass]
{
    let availableClasses : [AnyClass] = [ Class1.self, Class2.self ]

    var conformingClasses = Array<AnyClass>()

    for myClass : AnyClass in availableClasses
    {
        if myClass.conforms(to: proto)
        {
            conformingClasses.append(myClass)
        }
    }

    return conformingClasses
}

次に、上記の構造を次のように使用します。

let classes = classesConformingToProtocol(AlertProtocol.self)

作業を行うトリッキーな部分は、プロトコルをObjective Cランタイムに公開し、任意の「プロトコルタイプ」をパラメーターとして渡すことができる「@objc」です。

おそらく将来のある時点で、これを「純粋な」Swift方法で行うことができるようになるでしょう。

11
joakim

これが私が試したことです:

@objc protocol Walker
{
    func walk()
}

@objc protocol Runner
{
    func run()
}

@objc class Zombie : Walker
{
    func walk () { println("Brains...") }
}

@objc class Survivor : Runner
{
    func run() { println("Aaaah, zombies!") }
}

func classesConformingToProtocol(proto:Protocol) -> AnyClass[]
{
    let availableClasses : AnyClass[] = [ Zombie.self, Survivor.self ]

    var conformingClasses = Array<AnyClass>()

    for myClass : AnyClass in availableClasses
    {
        if myClass.conformsToProtocol(proto)
        {
            conformingClasses.append(myClass)
        }
    }

    return conformingClasses
}

// This does not work
let walkers = classesConformingToProtocol(Walker.self)
let runners = classesConformingToProtocol(Runner.self)

SwiftのMetatype情報をProtocolオブジェクトに変換できませんでした。

4
Erik

Swift 2.0では、以前は次のように使用していました。

classA.conformsToProtocol(XXXProtocol.self as! Protocol)

それはうまくいきません...

Protocol:の定義を見てください

// All methods of class Protocol are unavailable. 
// Use the functions in objc/runtime.h instead.

@available(iOS 2.0, *)
public class Protocol {
}

すべてが利用できません...そして私は代わりにobjc/runtime.hでどちらを使用するべきかわかりません

だから私はこの方法を使わなければなりません:

if ClassA is protocol<XXXProtocol> {
    // do something
}

現在、動作します...

1
Klein Mioke

使用を許可しない場合は@objc(たとえば、プロトコルにプロパティがあるため)、私が見つけた唯一の解決策はクロージャを使用することです。次に、クロージャを使用してプロトコルを使用し、値を返す必要があります。

protocol Proto { }
protocol Proto2 { }
class Foo: Proto { }
class Bar: Proto, Proto2 { }
class Baz: Proto2 { }
class Qux { }

func printConforms(classList: [AnyClass], protoCond: (AnyClass) -> Any?) {
    for i in classList {
        print(i, terminator: " -> ")
        if protoCond(i) != nil {
            print("is subscriber")
        } else {
            print("NOT IS subscriber")
        }
    }
}

let myClasses: [AnyClass] = [Foo.self, Bar.self, Baz.self, Qux.self]
printConforms(classList: myClasses, protoCond: { $0 as? Proto.Type })

より完全な例: https://Gist.github.com/brunomacabeusbr/eea343bb9119b96eed3393e41dcda0c9

編集

もう1つのより良い解決策は、ジェネリックを使用することです。次に例を示します。

protocol Proto { }
class Foo: Proto { }
class Bar: Proto { }
class Baz { }

func filter<T>(classes: [AnyClass], byConformanceTo: T.Type) -> [AnyClass] {
    return classes.filter { $0 is T }
}

filter(classes: [Foo.self, Bar.self, Baz.self], byConformanceTo: Proto.Type.self)
// return [Foo.self, Bar.self]
0
Macabeus