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
を渡しますが、コンパイルを取得しますエラー「式の型 '()'を型 'String'に変換できません」CastDAO.invokeService("test", withParams: ["test" : "test"], returningClass: CityInfo.self) { cityInfo in /*...*/
}
編集:
Holexで述べたようにobject_getClass
を使用してみましたが、次のようになりました:
エラー:「タイプ「CityInfo.Type」はプロトコル「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
anyクラスを本当に許可しない限り、AnyClass
を使用しないでください。この場合、returningClass
パラメーターとクロージャーのパラメーター間のリンクを表すため、正しい型はT.Type
になります。
実際、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 /*...*/
}
現在、2つの可能性があります。エラーが変数の1つに移動する(間違った部分があることを意味する)か、「式のタイプ()
をタイプ($T6) -> ($T6) -> $T5
に変換できません」のような不可解なメッセージを受け取ります。
後者のエラーの原因は、コンパイラが記述したタイプを推測できないことです。この場合、問題はT
がクロージャーのパラメーターでのみ使用され、渡されたクロージャーは特定の型を示していないため、コンパイラはどの型を推論するのかを知らないということです。 returningClass
のタイプを変更してT
を含めることにより、コンパイラーに汎用パラメーターを決定する方法を提供します。
この方法でAnyObject
のクラスを取得できます。
let myClass: AnyClass = type(of: self)
let myClass: AnyClass = object_getClass(self)
必要に応じて、後でパラメータとして渡すことができます。
私は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
}
}
obj-getclass
を使用します。
CastDAO.invokeService("test", withParams: ["test" : "test"], returningClass: obj-getclass(self)) { cityInfo in /*...*/
}
自分が都市情報オブジェクトであると仮定します。