クラス型を関数パラメーターとして渡す方法


146

Webサービスを呼び出し、JSON応答をシリアル化してオブジェクトに戻す汎用関数があります。

class func invokeService<T>(service: String, withParams params: Dictionary<String, String>, returningClass: AnyClass, completionHandler handler: ((T) -> ())) {

            /* Construct the URL, call the service and parse the response */
}

私が達成しようとしているのは、このJavaコードに相当するものです

public <T> T invokeService(final String serviceURLSuffix, final Map<String, String> params,
                               final Class<T> classTypeToReturn) {
}
  • 私のメソッド署名は、私が達成しようとしているものの正しいですか?
  • より具体的にはAnyClass、パラメーター型として指定することは正しいことですか?
  • メソッドを呼び出すMyObject.selfと、returningClass値として渡していますが、「式のタイプ '()'をタイプ 'String'に変換できません」というコンパイルエラーが発生します。
CastDAO.invokeService("test", withParams: ["test" : "test"], returningClass: CityInfo.self) { cityInfo in /*...*/

}

編集:

object_getClassholexで言及されているように、を使用してみましたが、次のようになりました。

エラー:「タイプ「CityInfo.Type」はプロトコル「AnyObject」に準拠していません」

プロトコルに準拠するために何をする必要がありますか?

class CityInfo : NSObject {

    var cityName: String?
    var regionCode: String?
    var regionName: String?
}

私はSwiftsのジェネリックがJavaのように機能するとは思わない。したがって、推論者はそれほどインテリジェントではありません。idはClass <T>の事柄を省略し、ジェネリック型を明示的に指定しますCastDAO.invokeService("test", withParams: ["test" : "test"]) { (ci:CityInfo) in }
クリスチャンディートリッヒ

回答:


144

あなたは間違った方法でそれに近づいている:スウィフトに、Objective-Cのとは異なり、クラスは、特定のタイプを持っているし、(クラス場合であっても継承階層を持っているBから継承Aその後は、B.Typeまたから継承しますA.Type):

class A {}
class B: A {}
class C {}

// B inherits from A
let object: A = B()

// B.Type also inherits from A.Type
let type: A.Type = B.self

// Error: 'C' is not a subtype of 'A'
let type2: A.Type = C.self

あなたが使用してはならない理由ですAnyClassあなたが本当に許可したい場合を除き、いずれかのクラスを。この場合、パラメータとクロージャのパラメータのT.Type間のリンクを表すため、正しいタイプはになりreturningClassます。

実際、代わりにそれをAnyClass使用すると、コンパイラーはメソッド呼び出しの型を正しく推論できます。

class func invokeService<T>(service: String, withParams params: Dictionary<String, String>, returningClass: T.Type, completionHandler handler: ((T) -> ())) {
    // The compiler correctly infers that T is the class of the instances of returningClass
    handler(returningClass())
}

Tに渡すのインスタンスを作成する際の問題があります。今すぐhandlerコードを実行しようとすると、コンパイラーTはで作成できないというメッセージを表示し()ます。そして当然のTことながら、特定の初期化子を実装するように明示的に制約する必要があります。

これは、次のようなプロトコルで実行できます。

protocol Initable {
    init()
}

class CityInfo : NSObject, Initable {
    var cityName: String?
    var regionCode: String?
    var regionName: String?

    // Nothing to change here, CityInfo already implements init()
}

次に、の一般的な制約をinvokeServiceから<T>に変更するだけです<T: Initable>

ヒント

「式のタイプ '()'をタイプ '文字列'に変換できません」などの奇妙なエラーが発生した場合、メソッド呼び出しのすべての引数を独自の変数に移動すると便利な場合があります。エラーの原因となっているコードを絞り込み、型推論の問題を明らかにするのに役立ちます。

let service = "test"
let params = ["test" : "test"]
let returningClass = CityInfo.self

CastDAO.invokeService(service, withParams: params, returningClass: returningClass) { cityInfo in /*...*/

}

現在、2つの可能性があります。エラーが変数の1つに移動する(つまり、誤った部分が存在する)か、「式の型()を型に変換できません」などの不可解なメッセージが表示されます($T6) -> ($T6) -> $T5

後者のエラーの原因は、コンパイラーがユーザーが書き込んだタイプを推測できないことです。この場合の問題はT、クロージャーのパラメーターでのみ使用され、渡されたクロージャーが特定の型を示さないため、コンパイラーが推測する型を認識できないことです。returningClass含めるタイプを変更することによりT、コンパイラーにジェネリックパラメーターを決定する方法を提供します。


T.Typeヒントをありがとう-引数としてクラス型を渡すために必要なものだけ。
エシュロン

最後の「ヒント」は私を救った
アレックス

<T:Initiable>テンプレート関数を使用する代わりに、
returningClassをInitiable.Type

元のコードでは、異なる結果が生成されていました。ジェネリックパラメータTは、returningClassとに渡されるオブジェクトとの関係を表すために使用されますcompletionHandler。場合Initiable.Typeに使用され、この関係は失われます。
EliaCereda

そして?ジェネリックスは書くことを許可しませんfunc somefunc<U>()
Gargo

34

AnyObjectこの方法でのクラスを取得できます:

Swift 3.x

let myClass: AnyClass = type(of: self)

Swift 2.x

let myClass: AnyClass = object_getClass(self)

必要に応じて、後でパラメータとして渡すことができます。


1
あなたもできるself.dynamicType
newacct

1
myClassを使おうとすると、「宣言されていないタイプ "myClass"の使用」というエラーが発生します。Swift3、XcodeおよびiOSの最新ビルド
混乱

@混乱、そのような問題はありません。コンテキストについての詳細を教えてください。
holex

ボタンを作っています。そのボタンのコード(SpriteKitなので、touchesBeganの内部)では、ボタンでClass関数を呼び出したいので、そのクラスへの参照が必要です。ボタンクラスで、クラスへの参照を保持する変数を作成しました。しかし、適切な表現や用語が何であれ、クラス、またはそのクラスであるタイプを保持することはできません。インスタンスへの参照を格納するためにのみ取得できます。できれば、クラスタイプへの参照をボタンに渡したいと思います。しかし、私は内部参照の作成を始めたばかりで、それを機能させることさえできませんでした。
混乱

それは方法論の継続であり、私がここで使用していたと考えていました:stackoverflow.com/questions/41092440/…。それ以来、プロトコルでいくつかのロジックを機能させることができましたが、より単純なメカニズムで何ができるかを理解するために、関連する型とジェネリックを理解する必要があるという事実にすぐに立ち向かいます。または、たくさんのコードをコピーして貼り付けます。これが私が通常行うことです;)
混乱している

7

私はswift5に同様のユースケースがあります:

class PlistUtils {

    static let shared = PlistUtils()

    // write data
    func saveItem<T: Encodable>(url: URL, value: T) -> Bool{
        let encoder = PropertyListEncoder()
        do {
            let data = try encoder.encode(value)
            try data.write(to: url)
            return true
        }catch {
            print("encode error: \(error)")
            return false
        }
    }

    // read data

    func loadItem<T: Decodable>(url: URL, type: T.Type) -> Any?{
        if let data = try? Data(contentsOf: url) {
            let decoder = PropertyListDecoder()
            do {
                let result = try decoder.decode(type, from: data)
                return result
            }catch{
                print("items decode failed ")
                return nil
            }
        }
        return nil
    }

}

同様のことを実行しようとしていますが、コールサイトでエラーが発生しますStatic method … requires that 'MyDecodable.Type' conform to 'Decodable'。への呼び出し例を示すために、回答を更新していただけませんloadItemか?
バリージョーンズ

これが、.Type.self(タイプとメタタイプ)の違いを学ばなければならないポイントです。
バリージョーンズ

0

使用obj-getclass

CastDAO.invokeService("test", withParams: ["test" : "test"], returningClass: obj-getclass(self)) { cityInfo in /*...*/

}

自分自身が都市情報オブジェクトであると仮定します。


0

スウィフト5

まったく同じ状況ではありませんが、同様の問題がありました。最後に私を助けたのはこれでした:

func myFunction(_ myType: AnyClass)
{
    switch type
    {
        case is MyCustomClass.Type:
            //...
            break

        case is MyCustomClassTwo.Type:
            //...
            break

        default: break
    }
}

次に、このクラスのインスタンス内で次のように呼び出すことができます。

myFunction(type(of: self))

これが私の同じ状況の誰かを助けることを願っています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.