Bir sınıf türü bir işlev parametresi olarak nasıl iletilir


148

Bir web hizmetini çağıran ve JSON yanıtını bir nesneye geri serileştiren genel bir işlevim var.

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

Başarmaya çalıştığım şey, bu Java kodunun eşdeğeri

public <T> T invokeService(final String serviceURLSuffix, final Map<String, String> params,
                               final Class<T> classTypeToReturn) {
}
  • Yapmaya çalıştığım şey için yöntem imzam doğru mu?
  • Daha spesifik olarak, AnyClassyapılacak doğru şeyi bir parametre türü olarak belirtmek mi?
  • Yöntemi çağırırken, MyObject.selfreturnningClass değeri olarak geçiyorum , ancak bir derleme hatası alıyorum "İfadenin türü '()', 'String' türüne dönüştürülemiyor"
CastDAO.invokeService("test", withParams: ["test" : "test"], returningClass: CityInfo.self) { cityInfo in /*...*/

}

Düzenle:

object_getClassHolex'in bahsettiği gibi kullanmayı denedim , ama şimdi alıyorum:

hata: "'CityInfo.Type' yazın 'AnyObject' protokolüne uymuyor"

Protokole uymak için ne yapılması gerekiyor?

class CityInfo : NSObject {

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

Swifts generics'in javas gibi çalıştığını düşünmüyorum. bu nedenle çıkarıcı o kadar zeki olamaz. id Class <T> Thing'i atlayın ve Jenerik Tipi açıkça belirtinCastDAO.invokeService("test", withParams: ["test" : "test"]) { (ci:CityInfo) in }
Christian Dietrich

Yanıtlar:


144

Yanlış bir şekilde yaklaşıyorsunuz: Swift'de, Objective-C'nin aksine, sınıfların belirli türleri vardır ve hatta bir miras hiyerarşisi vardır (yani, sınıf Bmiras alıyorsa A, o zaman B.Typeda miras alır 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

Bu yüzden herhangi bir sınıfa AnyClassgerçekten izin vermek istemediğiniz sürece kullanmamalısınız . Bu durumda, doğru tip , parametre ile kapanış parametresi arasındaki bağlantıyı ifade ettiği için olacaktır.T.TypereturningClass

Aslında, bunun yerine kullanmak AnyClass, derleyicinin yöntem çağrısındaki türleri doğru bir şekilde çıkarmasına izin verir:

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

Şimdi T, geçiş için bir örnek oluşturma sorunu var handler: kodu hemen çalıştırmaya çalışırsanız, derleyici Tile inşa edilemeyen bir şikayet olacaktır (). Ve haklı olarak öyle: Tbelirli bir başlatıcıyı gerçekleştirmesini gerektirecek şekilde açıkça kısıtlanmalıdır.

Bu, aşağıdaki gibi bir protokol ile yapılabilir:

protocol Initable {
    init()
}

class CityInfo : NSObject, Initable {
    var cityName: String?
    var regionCode: String?
    var regionName: String?

    // Nothing to change here, CityInfo already implements init()
}

O zaman sadece genel kısıtlamalarını ' invokeServiceden olarak <T>değiştirmelisiniz <T: Initable>.

İpucu

"İfadenin türü '()' 'Dize' türüne dönüştürülemiyor" gibi garip hatalar alırsanız, yöntem çağrısının her bağımsız değişkenini kendi değişkenine taşımak genellikle yararlıdır. Hataya neden olan kodu daraltmaya ve tür çıkarım sorunlarını ortaya çıkarmaya yardımcı olur:

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

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

}

Değişkenlerden birine hata hamle (ki araçlar yanlış parçası olduğunu) ya da "ifadenin türünü dönüştürülemez gibi şifreli mesaj çıktı: Şimdi orada iki olasılık vardır ()yazmanız ($T6) -> ($T6) -> $T5".

İkinci hatanın nedeni, derleyicinin yazdığınız şeyin türlerini çıkaramamasıdır. Bu durumda sorun, Tyalnızca kapama parametresinde kullanılan ve geçirdiğiniz kapanış belirli bir türü göstermediği için derleyicinin hangi türden çıkarım yapacağını bilmemesidir. Türünü değiştirerek returningClassdahil etmek TEğer derleyici genel parametreyi belirlemek için bir yol sunar.


T.Type ipucu için teşekkürler - tam da bir sınıf türünü bağımsız değişken olarak geçirmek için ihtiyacım olan şey.
Echelon

Sondaki "İpucu" beni kurtardı
Alex

<T: Initiable> şablonlu bir işlev kullanmak yerine, returnningClass'ı bir Initiable olarak geçirmek de mümkündür.Type
Devous

Orijinal kodda farklı sonuçlar doğuracaktı. Genel parametre T, returningClassiletilen nesne ile arasındaki ilişkiyi ifade etmek için kullanılır completionHandler. Eğer Initiable.Typekullanılan bu ilişki kaybedilir.
EliaCereda

VE? Jenerikler yazmaya izin vermiyorfunc somefunc<U>()
Gargo

34

sınıfını AnyObjectşu yolla alabilirsiniz:

Swift 3.x

let myClass: AnyClass = type(of: self)

Swift 2.x

let myClass: AnyClass = object_getClass(self)

ve isterseniz bunu daha sonra paramater olarak geçirebilirsiniz.


1
ayrıca yapabilirsinizself.dynamicType
newacct

1
MyClass'ı her kullanmaya çalıştığımda, "bildirilmemiş" myClass türü kullanımı "hatasına neden oluyor. Swift 3, Xcode ve iOS'un en son sürümleri
Confused

@Confused, bunda böyle bir sorun görmüyorum, bana bağlam hakkında daha fazla bilgi vermeniz gerekebilir.
holex

Bir düğme yapıyorum. Bu düğmenin kodunda (bu SpriteKit, yani touchesBegan'ın içinde) Düğmenin bir Sınıf işlevi çağırmasını istiyorum, bu yüzden bu sınıfa bir referansa ihtiyacım var. Bu yüzden Button Sınıfında, Sınıfa bir referans tutmak için bir değişken oluşturdum. Ancak, doğru ifade / terminoloji ne olursa olsun, bir Sınıfı veya bu Sınıf olan Türü tutmasını sağlayamıyorum. Yalnızca örneklere referansları depolamak için alabilirim. Tercihen, Sınıf Türüne ilişkin bir referansı düğmeye iletmek istiyorum. Ancak dahili bir referans oluşturmaya yeni başladım ve bunu çalıştırmayı bile başaramadım.
Confused

Burada kullandığım metodoloji ve düşüncenin devamı niteliğindeydi: stackoverflow.com/questions/41092440/… . O zamandan beri Protokoller ile çalışan bir mantık edindim, ancak hızlı bir şekilde karşı karşıya geliyorum, daha basit mekanizmalarla yapabileceğimi düşündüğüm şeyi elde etmek için ilişkili türleri ve jenerikleri de bulmam gerekecek. Ya da çok sayıda kodu kopyalayıp yapıştırın. Normalde yaptığım şey bu;)
karıştı

9

Swift5'te benzer bir kullanım durumum var:

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
    }

}


Benzer bir şey yapmak çalışıyorum ama callsite bir hata alıyorum: Static method … requires that 'MyDecodable.Type' conform to 'Decodable'. Örnek bir çağrı vermek için cevabınızı günceller misiniz loadItem?
Barry Jones

.TypeVe .self(tür ve metatip) arasındaki farkı öğrenmem gereken nokta bu .
Barry Jones

0

Kullanım obj-getclass:

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

}

Benliğin bir şehir bilgi nesnesi olduğunu varsaymak.


0

Swift 5

Tam olarak aynı durum değil, ama benzer bir sorun yaşıyordum. Sonunda bana yardımcı olan şuydu:

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

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

        default: break
    }
}

Daha sonra, bunu adı geçen sınıfın bir örneğinin içinde şöyle çağırabilirsiniz:

myFunction(type(of: self))

Umarım bu aynı durumdaki birine yardımcı olur.


0

Son zamanlarda, UINavigationController'ımı alt görünüm düğmeleri hariç her şeye görünmez hale getirmenin bir yolunu ararken rastladım. Bunu özel bir nav denetleyicisine koydum:

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

Çağrı yapmadan önce nokta dönüştürüldüğünden çerçeve yerine sınır kullanmayı unutmayın.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.