फंक्शन पैरामीटर के रूप में क्लास टाइप कैसे पास करें


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 */
}

मैं जो पूरा करने की कोशिश कर रहा हूं वह इस जावा कोड के बराबर है

public <T> T invokeService(final String serviceURLSuffix, final Map<String, String> params,
                               final Class<T> classTypeToReturn) {
}
  • क्या मैं सही को पूरा करने के लिए जो कोशिश कर रहा हूं, उसके लिए मेरी विधि हस्ताक्षर है?
  • विशेष रूप से, AnyClassपैरामीटर के रूप में निर्दिष्ट करना सही चीज़ है?
  • विधि को कॉल करते समय, मैं MyObject.selfरिटर्निंगक्लास मान के रूप में पास कर रहा हूं , लेकिन मुझे एक संकलन त्रुटि मिलती है "अभिव्यक्ति के प्रकार को परिवर्तित नहीं कर सकता '()' टाइप करने के लिए 'स्ट्रिंग'"
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?
}

मुझे नहीं लगता कि स्विफ्ट जेनेरिक जॉज़ की तरह काम करती है। इस प्रकार अधमरा वह बुद्धिमान नहीं हो सकता। आईडी कक्षा <T> को छोड़ दें और सामान्य प्रकार को स्पष्ट रूप से निर्दिष्ट करेंCastDAO.invokeService("test", withParams: ["test" : "test"]) { (ci:CityInfo) in }
क्रिश्चियन डिट्रिच

जवाबों:


144

आप इसे गलत तरीके से ले जा रहे हैं: स्विफ्ट में, ऑब्जेक्टिव-सी के विपरीत, वर्गों में विशिष्ट प्रकार होते हैं और यहां तक ​​कि एक वंशानुक्रम पदानुक्रम भी होता है (यानी, यदि वर्ग 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आप कंपाइलर को जेनेरिक पैरामीटर निर्धारित करने का एक तरीका देते हैं।


T.Type संकेत के लिए धन्यवाद - एक तर्क के रूप में एक वर्ग प्रकार पारित करने के लिए बस मुझे क्या चाहिए।
इकलौती

अंत में "टिप" ने मुझे बचा लिया
एलेक्स

इसके बजाय <T: Initiable> templated फ़ंक्शन का उपयोग करने के बजाय, यह वापसी को पास करने के लिए भी संभव है। Initiable.Type
Devous

मूल कोड में जो विभिन्न परिणामों का उत्पादन किया होगा। सामान्य पैरामीटर Tका उपयोग returningClassउस वस्तु और उसके बीच के संबंध को व्यक्त करने के लिए किया जाता है completionHandler। अगर Initiable.Typeइस्तेमाल किया जाता है तो यह रिश्ता खो जाता है।
एलियाकेरेडा

तथा? जेनरिक लिखने की अनुमति नहीं देते हैंfunc somefunc<U>()
गार्गो

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, Xcode और iOS के नवीनतम बिल्ड
कन्फ्यूज्ड

@Confused, मुझे ऐसा कोई मुद्दा दिखाई नहीं दे रहा है, आपको मुझे संदर्भ के बारे में अधिक जानकारी देने की आवश्यकता हो सकती है।
होलेक्स

मैं एक बटन बना रहा हूं। उस बटन के कोड में (यह स्प्राइटकिट है, इसलिए टचबायगन के अंदर) मैं चाहता हूं कि बटन एक क्लास फ़ंक्शन को कॉल करे, इसलिए उस क्लास के संदर्भ की आवश्यकता है। इसलिए बटन कक्षा में मैंने कक्षा का संदर्भ रखने के लिए एक चर बनाया है। लेकिन मैं इसे एक वर्ग, या प्रकार जो कि वर्ग है, जो भी सही शब्द / शब्दावली है, पकड़ नहीं सकता। मैं इसे केवल उदाहरणों के संदर्भों को संग्रहीत करने के लिए प्राप्त कर सकता हूं। अधिमानतः, मैं बटन में वर्ग प्रकार का संदर्भ देना चाहूंगा। लेकिन मैंने सिर्फ एक आंतरिक संदर्भ बनाने के साथ शुरुआत की, और वह काम भी नहीं कर सका।
कन्फ्यूज्ड

यह पद्धति और सोच पर एक निरंतरता थी जो मैं यहां उपयोग कर रहा था: stackoverflow.com/questions/41092440/… । जब से मैंने प्रोटोकॉल के साथ काम करने वाले कुछ तर्क दिए हैं, लेकिन तेजी से इस तथ्य के खिलाफ आ रहा हूं कि मुझे संबंधित प्रकारों और जेनेरिकों का पता लगाने की आवश्यकता है जो मुझे लगा कि मैं सरल तंत्र के साथ क्या कर सकता हूं। या बस बहुत सारे कोड को कॉपी और पेस्ट करें। जो मैं आम तौर पर करता हूं;)
कन्फ्यूज्ड

7

मेरे पास स्विफ्ट 5 में एक समान उपयोग का मामला है:

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))

आशा है कि यह मेरी इसी स्थिति में किसी की मदद करता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.