programing

클래스 유형을 함수 매개 변수로 전달하는 방법

iphone6s 2023. 5. 27. 09:55
반응형

클래스 유형을 함수 매개 변수로 전달하는 방법

나는 웹 서비스를 호출하고 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 */
}

제가 달성하고자 하는 것은 이 자바 코드와 동등한 것입니다.

public <T> T invokeService(final String serviceURLSuffix, final Map<String, String> params,
                               final Class<T> classTypeToReturn) {
}
  • 수행하려는 작업에 대한 메서드 서명이 정확합니까?
  • 더 구체적으로 , 는 보구체으로, 는다같지니다합정이를 지정하는 입니다.AnyClass매개 변수로 올바른 작업을 입력하십시오.
  • 메소드를 부를 때, 저는 통과합니다.MyObject.self반환되는 Class 값으로, 그러나 컴파일 오류가 발생합니다. "식의 유형 '(")을 유형 'String'으로 변환할 수 없습니다."
CastDAO.invokeService("test", withParams: ["test" : "test"], returningClass: CityInfo.self) { cityInfo in /*...*/

}

편집:

사용해 보았습니다.object_getClass홀렉스가 말한 것처럼, 하지만 이제 나는 이해합니다.

오류: "'CityInfo'를 입력합니다.Type'이(가) 'AnyObject' 프로토콜과 맞지 않습니다."

프로토콜을 준수하려면 무엇을 해야 합니까?

class CityInfo : NSObject {

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

에서는 Objective-C와상속 계층( 방접고근있습하니다로식으된잘못▁class▁(that있▁you다니습▁have▁hierarchyance▁an▁have▁inherit접고방하근식된으로▁swift▁classes,▁types잘▁even▁and못▁specific▁objective)을 가지고 있습니다. 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>.

팁.

"식의 유형 '(')을 '문자열'로 변환할 수 없습니다."와 같은 이상한 오류가 발생하면 메서드 호출의 모든 인수를 고유 변수로 이동하는 것이 유용한 경우가 많습니다.오류의 원인이 되는 코드를 좁히고 유형 추론 문제를 찾는 데 도움이 됩니다.

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컴파일러에 제네릭 매개 변수를 결정하는 방법을 제공합니다.

당신은 의 수업을 받을 수 있습니다.AnyObject이 방법을 통해:

스위프트 3.x

let myClass: AnyClass = type(of: self)

스위프트 2.x

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
    }

}

여기에 있는 각 코드를 swift 파일에 복사하여 붙여넣기만 하면 됩니다.

다른 이름으로 저장: APICaller.swift

import Foundation

struct APICaller
{
    public static func get<T: Decodable>(url: String, receiveModel: T.Type, completion:@escaping (Decodable) -> ())
    {
        send(url: url, json: nil, receiveModel: receiveModel, completion: completion, httpMethod: "GET")
    }
    
    public static func post<T: Decodable>(url: String, json: [String: Any]?, receiveModel: T.Type, completion:@escaping (Decodable) -> ())
    {
        send(url: url, json: nil, receiveModel: receiveModel, completion: completion, httpMethod: "POST")
    }
    
    public static func delete<T: Decodable>(url: String, json: [String: Any]?, receiveModel: T.Type, completion:@escaping (Decodable) -> ())
    {
        send(url: url, json: nil, receiveModel: receiveModel, completion: completion, httpMethod: "DELETE")
   }

    private static func send<T: Decodable>(url: String, json: [String: Any]?, receiveModel: T.Type, completion:@escaping (Decodable) -> (), httpMethod: String)
    {
        // create post request
        let urlURL: URL = URL(string: url)!
        var httpRequest: URLRequest = URLRequest(url: urlURL)
        httpRequest.httpMethod = httpMethod
        
        if(json != nil)
        {
            // serialize map of strings to json object
            let jsonData: Data = try! JSONSerialization.data(withJSONObject: json!)
            // insert json data to the request
            httpRequest.httpBody = jsonData
            httpRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
        }
        
        // create an asynchronus task to post the request
        let task = URLSession.shared.dataTask(with: httpRequest)
        { jsonData, response, error in
            // on callback parse the json into the receiving model object
            let receivedModelFilled: Decodable = Bundle.main.decode(receiveModel, from: jsonData!)

            // cal the user callback with the constructed object from json
            DispatchQueue.main.async {
                completion(receivedModelFilled)
            }
        }
        task.resume()
    }
}

다음으로 저장:테스트 서비스.swift

import Foundation

struct TestService: Codable
{
    let test: String
}

그러면 다음과 같이 사용할 수 있습니다.

let urlString: String = "http://localhost/testService"  <--- replace with your actual service url

// call the API in post request
APICaller.post(url: urlString, json: ["test": "test"], receiveModel: TestService.self, completion: { testReponse in
    // when response is received - do something with it in this callback
    let testService: TestService = testReponse as! TestService
    print("testService: \(testService)")
})

팁: 저는 온라인 서비스를 사용하여 제 JSON을 신속한 파일로 변환하기 때문에 제가 남은 것은 전화를 쓰고 응답을 처리하는 것입니다. 저는 이것을 사용합니다: https://app.quicktype.io 하지만 당신은 당신이 선호하는 것을 검색할 수 있습니다.

스위프트 5

정확히 같은 상황은 아니지만, 저도 비슷한 문제를 겪고 있었습니다.결국 도움이 된 것은 다음과 같습니다.

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

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

        default: break
    }
}

그런 다음 이 클래스를 다음과 같은 클래스의 인스턴스 내에서 호출할 수 있습니다.

myFunction(type(of: self))

이것이 저와 같은 상황에 있는 누군가에게 도움이 되기를 바랍니다.

최근에 UINavigation Controller를 하위 보기 버튼을 제외한 모든 항목에서 볼 수 없도록 만드는 방법을 찾고 있었습니다.사용자 지정 탐색 컨트롤러에 넣었습니다.

// MARK:- UINavigationBar Override
private extension UINavigationBar {
    
    override open func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
        // Make the navigation bar ignore interactions unless with a subview button
        return self.point(inside: point, with: event, type: UIButton.self)
    }
    
}

// MARK:- Button finding hit test
private extension UIView {
    
    func point<T: UIView>(inside point: CGPoint, with event: UIEvent?, type: T.Type) -> Bool {
        
        guard self.bounds.contains(point) else { return false }
        
        if subviews.contains(where: { $0.point(inside: convert(point, to: $0), with: event, type: type) }) {
            return true
        }

        return self is T
    }
    
}

호출하기 전에 점이 변환되므로 프레임 대신 경계를 사용하는 것을 잊지 마십시오.

사용:

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

}

자신을 도시 정보 객체로 가정합니다.

언급URL : https://stackoverflow.com/questions/24308975/how-to-pass-a-class-type-as-a-function-parameter

반응형