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


146

웹 서비스를 호출하고 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.selfreturningClass 값으로 전달 되지만 컴파일 오류 "식의 유형 '()'을 '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 제네릭이 자바처럼 작동한다고 생각하지 않습니다. 따라서 추론자는 그렇게 똑똑 할 수 없습니다. id <Class>를 생략하고 명시 적으로 제네릭 형식을 지정합니다CastDAO.invokeService("test", withParams: ["test" : "test"]) { (ci:CityInfo) in }
Christian Dietrich

답변:


144

당신은 잘못된 방법으로 접근하고있다 : 스위프트에, 오브젝티브 C는 달리, 클래스가 특정 유형이, 심지어 상속 계층 구조를 가지고 (즉, 경우에 클래스 B에서 상속 AB.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컴파일러에 일반 매개 변수를 결정하는 방법이 제공됩니다.


T.Type 힌트에 감사드립니다-클래스 유형을 인수로 전달하는 데 필요한 것입니다.
Echelon

끝에 "팁"은 나를 구했다
Alex

<T : Initiable> 템플릿 함수를 사용하는 대신 returningClass를 Initiable.로 전달할 수도 있습니다. Type
Devous

다른 결과를 가져온 원래 코드에서. 일반 매개 변수 T는에 returningClass전달 된 객체와 객체 사이의 관계를 표현하는 데 사용 됩니다 completionHandler. 경우 Initiable.Type사용이 관계가 손실됩니다.
EliaCereda

과? 제네릭은 글을 쓸 수 없습니다func somefunc<U>()
Gargo

34

AnyObject이 방법으로 클래스를 얻을 수 있습니다 .

스위프트 3.x

let myClass: AnyClass = type(of: self)

스위프트 2.x

let myClass: AnyClass = object_getClass(self)

원하는 경우 나중에 매개 변수로 전달할 수 있습니다.


1
당신은 또한 할 수 있습니다self.dynamicType
newacct

1
. 나는 그것이 myClass가 ""신고되지 않은 유형의 사용 "의 오류를 일으키는 myClass가 사용하려고 할 때마다 스위프트 3, 최신은 엑스 코드 및 iOS 빌드
혼란

@Confused, 나는 그런 문제가 없다고 생각한다. 컨텍스트에 대한 더 많은 정보를 제공해야 할 수도있다.
holex

버튼을 만들고 있습니다. 해당 버튼의 코드 (SpriteKit이므로 touchesBegan 내부) 버튼이 클래스 함수를 호출하기를 원하므로 해당 클래스에 대한 참조가 필요합니다. 그래서 버튼 클래스에서 클래스에 대한 참조를 보유하는 변수를 만들었습니다. 그러나 올바른 단어 / 용어가 무엇이든 클래스 또는 클래스 인 유형을 보유 할 수는 없습니다. 인스턴스에 대한 참조 만 저장하도록 할 수 있습니다. 바람직하게는 클래스 유형에 대한 참조를 버튼에 전달하고 싶습니다. 그러나 방금 내부 참조를 만드는 것으로 시작하여 그 작업을 수행 할 수도 없었습니다.
혼란

방법론과 계속 여기에서 사용하고 있다고 생각했습니다 : 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
    }

}

비슷한 것을하려고하지만 callsite에서 오류가 발생합니다 Static method … requires that 'MyDecodable.Type' conform to 'Decodable'. 예제 호출을 제공하기 위해 답변을 업데이트 하시겠습니까 loadItem?
Barry Jones

이 밝혀 내가 사이의 차이 배워야 할 점이다 .Type.self(종류와 메타 타입을).
Barry Jones

0

사용 obj-getclass:

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

}

self는 도시 정보 객체라고 가정합니다.


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.