1. ホーム
  2. swift

[解決済み] 関数のパラメータとしてクラス型を渡す方法

2022-05-04 22:36:15

質問内容

ウェブサービスを呼び出し、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 をreturnClassの値として使用しますが、コンパイル時に error "式の型 '()' を 'String'"' 型に変換できません。
CastDAO.invokeService("test", withParams: ["test" : "test"], returningClass: CityInfo.self) { cityInfo in /*...*/

}


編集する

を使ってみました。 object_getClass , holexさんのおっしゃるとおりですが、今は、こうなります。

error: "Type 'CityInfo.Type' does not conform to protocol 'AnyObject'"

プロトコルに準拠するために必要なことは何ですか?

class CityInfo : NSObject {

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

解決方法は?

Swiftでは、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> .

チップ

式の型 '()' を 'String' 型に変換できない "のような奇妙なエラーが発生した場合、メソッド呼び出しのすべての引数を独自の変数に移動させると便利なことがよくあります。これは、エラーの原因となっているコードを絞り込み、型推論の問題を明らかにするのに役立ちます。

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

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

}

エラーが変数のひとつに移動するか(これは間違った部分がそこにあることを意味します)、あるいは "式の型を変換できません、というような不可解なメッセージが表示されるかのどちらかです。 () をタイプ ($T6) -> ($T6) -> $T5 となります。

後者のエラーの原因は、書いたものの型をコンパイラが推論できていないことです。この場合、問題は T はクロージャのパラメータにしか使われておらず、渡されたクロージャは特定の型を示さないので、コンパイラはどの型を推論すればいいのかわからない。の型を変更することで returningClass を含めるようにしました。 T を指定すると、コンパイラがジェネリックパラメータを決定するための方法を提供します。