Swift'de farklı türle süper sınıf özelliğini geçersiz kılma


129

Swift'de birisi, bir süper sınıfın bir özelliğinin orijinal özellikten alt sınıflara ayrılmış başka bir nesneyle nasıl geçersiz kılınacağını açıklayabilir mi?

Şu basit örneği ele alalım:

class Chassis {}
class RacingChassis : Chassis {}

class Car {
    let chassis = Chassis()
}
class RaceCar: Car {
    override let chassis = RacingChassis() //Error here
}

Bu şu hatayı verir:

Cannot override with a stored property 'chassis'

Bunun yerine 'var' olarak kasam varsa, şu hatayı alıyorum:

Cannot override mutable property 'chassis' of type 'Chassis' with covariant type 'RacingChassis'

"Geçersiz Kılma Özellikleri" altındaki kılavuzda bulabildiğim tek şey, alıcı ve ayarlayıcıyı geçersiz kılmamız gerektiğine işaret ediyor, bu da mülkün değerini değiştirmek için işe yarayabilir (eğer 'var' ise), peki ya özellik sınıfını değiştirmek? ?

Yanıtlar:


115

Swift, herhangi bir değişkenin veya özelliğin sınıf türünü değiştirmenize izin vermez. Bunun yerine, yeni sınıf türünü işleyen alt sınıfta fazladan bir değişken oluşturabilirsiniz:

class Chassis {}
class RacingChassis : Chassis {}

class Car {
    var chassis = Chassis()
}
class RaceCar: Car {
    var racingChassis = RacingChassis()
    override var chassis: Chassis {
        get {
            return racingChassis
        }
        set {
            if let newRacingChassis = newValue as? RacingChassis {
                racingChassis = newRacingChassis
            } else {
                println("incorrect chassis type for racecar")
            }
        }
    }
}

Let sözdizimi ile bir özellik bildirilemez ve alt sınıfındaki var ile geçersiz kılınamaz veya bunun tersi, süper sınıf uygulamasının bu özelliğin başlatıldıktan sonra değişmesini beklememesi olabilir. Bu durumda, özelliğin üst sınıfta 'var' ile ve alt sınıfla eşleşmesi için (yukarıdaki ön bilgide gösterildiği gibi) bildirilmesi gerekir. Üst sınıftaki kaynak kodu değiştirilemezse, muhtemelen en iyisi mevcut RaceCar'ı yok etmek ve şasinin her mutasyona uğratılması gerektiğinde yeni bir RaceCar oluşturmaktır.


1
Üzgünüm az önce yorumumu sildim ve sonra cevabınızı gördüm. Bir sorun yaşıyordum çünkü benim gerçek durumumda süper sınıfım bir strongözelliği olan bir Objective-C sınıfı ve onu geçersiz kılmaya çalışırken bir hata alıyordum - ancak bunun "örtük olarak açılmamış isteğe bağlı" ( chassis!) Swift, bu yüzden override var chassis : Chassis!düzeltir.
James

4
Bu artık Xcode 6.1 altında Swift 1.1 ile çalışmıyor. Hata oluşturur: "Değişmez 'let' özelliği 'kasa' bir 'var' alıcısıyla geçersiz kılınamaz". Daha iyi bir çözüm için herhangi bir fikriniz var mı?
Darrarski

1
Ayrıca Xcode 6.3 beta altında Swift 1.2'de artık çalışmıyor. '
Type2

10
Bu gerçekten berbat ve Swift'in başarısızlığı, imo. RacingChassis yana olan bir Şasi, derleyici, bir alt sınıfta özelliğin sınıfını rafine sağlayan bir sorunu olmaması gerekir. Pek çok dil buna izin verir ve desteklememek, bunun gibi çirkin geçici çözümlere yol açar. Alınma. : /
devios1

1
Opsiyonel ifadeler, bir değişkenin artık beklenen değeri döndürmeyen bir işlev tarafından sağlandığı için mevcut olmayabileceğini öne sürmenin bir yolunu sağlar. İşlev geçersiz kılındıysa bu tür durumlar meydana gelebilir. Detaylar için cevabıma bir göz atın.

10

Bu işe yarıyor gibi görünüyor

class Chassis {
    func description() -> String {
        return "Chassis"
    }
}
class RacingChassis : Chassis {
    override func description() -> String {
        return "Racing Chassis"
    }

    func racingChassisMethod() -> String {
        return "Wrooom"
    }
}

class Car {
    let chassis = Chassis()
}
class RaceCar: Car {
    override var chassis: RacingChassis {
    get {
        return self.chassis
    }
    set {
        self.chassis = newValue
    }
    }
}

var car = Car()
car.chassis.description()

var raceCar = RaceCar()
raceCar.chassis.description()
raceCar.chassis.racingChassisMethod()

1
Şasi özellik olarak tanımlanır gibi çalışır letiçinde Carimkansız değiştirmeyi mümkün kılar sınıfın. Sadece sınıftaki chassisözelliği değiştirebiliriz RaceCar.
David Arve

4
Bu Swift 2.0 için çalışmıyor. "Hata: değişmez 'let' özelliği 'kasa' bir 'var'
alıcısıyla geçersiz kılınamaz

Bu, hızlı 4.0'da da çalışmaz. katman yürütme başarısız: hata: MyPlaygrounds.playground: 14: 15: hata: 'var' geçersiz kılma alıcısı ile değiştirilemez 'izin' özelliği 'kasası' geçersiz kılınamaz: RacingChassis {^ MyPlaygrounds.playground: 11: 6: note : burada özelliği geçersiz kılmaya çalışın let chassis = Chassis () ^
karim

7

Bunu dene:

class Chassis{
     var chassis{
         return "chassis"
     } 
}

class RacingChassis:Chassis{
     var racing{
         return "racing"
     } 
}

class Car<Type:Chassis> {
     let chassis: Type
     init(chassis:Type){
        self.chassis = chassis
     }
}

class RaceCar: Car<RacingChassis> {
     var description{
         return self.chassis.racing
     } 
}

Sonra:

let racingChassis = RacingChassis()
let raceCar = RaceCar(chassis:racingChassis)
print(raceCar.description) //output:racing

Ayrıntılı bilgi http://www.mylonly.com/14957025459875.html


Ah temiz ve temiz
Inder Kumar Rathore

3

Sağlanan Solution Dash, süper sınıfın var yerine let anahtar sözcüğüyle bildirilmesi gerekmesi dışında iyi çalışır. İşte mümkün olan ancak TAVSİYE EDİLMEYEN bir çözüm!

Aşağıdaki çözüm Xcode 6.2, SWIFT 1.1 ile derlenecektir (eğer tüm sınıflar farklı hızlı dosyalar içindeyse), ancak bundan kaçınılmalıdır çünkü BEKLENMEYEN DAVRANIŞLARA YOL AÇABİLİR (özellikle isteğe bağlı olmayan türleri kullanırken BİR ÇARPMA DAHİL). NOT: BU XCODE 6.3 BETA 3, SWIFT 1.2 İLE ÇALIŞMAZ

class Chassis {}
class RacingChassis : Chassis {}
class Car {
    var chassis:Chassis? = Chassis()
}

class RaceCar: Car {
    override var chassis: RacingChassis? {
        get {
            return super.chassis as? RacingChassis
        }
        set {
            super.chassis = newValue
        }
    }
}

1
Swift 2.0 için çalışmıyor. "Şasi?" Türünde "değiştirilebilir özellikli 'kasa' geçersiz kılınamaz? kovaryant türü 'RacingChassis?' "ile
mohamede1945

2

Teorik olarak, bu şekilde yapmanıza izin verilir ...

class ViewController {

    var view: UIView! { return _view }

    private var _view: UIView!
}

class ScrollView : UIView {}

class ScrollViewController : ViewController {

    override var view: ScrollView! { return super.view as ScrollView! }
}

class HomeView : ScrollView {}

class HomeViewController : ScrollViewController {

    override var view: HomeView! { return super.view as HomeView! }
}

Bu, bir Xcode oyun alanında mükemmel çalışır.

Ancak , bunu gerçek bir projede denerseniz, bir derleyici hatası size şunu söyler:

Bildirim 'görünümü' birden fazla üst sınıf bildirimini geçersiz kılamaz

Şu an için yalnızca Xcode 6.0 GM'yi kontrol ettim.

Ne yazık ki, Apple bunu düzeltene kadar beklemeniz gerekecek.

Ben de bir hata raporu gönderdim. 18518795


Bu ilginç bir çözüm ve 6.1'de çalışıyor.
Chris Conover

1

İşlevler yerine değişkenler kullanarak bir API tasarlamanın neden sorunlu olduğuna dair birçok neden gördüm ve hesaplanmış özellikleri kullanmak bana geçici bir çözüm gibi geliyor. Örnek değişkenlerinizi kapsüllenmiş halde tutmak için iyi nedenler vardır. Burada Arabanın uyduğu bir Otomobil protokolü oluşturdum. Bu protokol, bir Chassis nesnesi döndüren bir erişimci yöntemine sahiptir. Araba buna uygun olduğundan, RaceCar alt sınıfı onu geçersiz kılabilir ve farklı bir Şasi alt sınıfı döndürebilir. Bu, Car sınıfının bir arabirime (Otomobil) programlamasına olanak tanır ve RacingChassis hakkında bilgi sahibi olan RaceCar sınıfı, _racingChassis değişkenine doğrudan erişebilir.

class Chassis {}
class RacingChassis: Chassis {}

protocol Automobile {
    func chassis() -> Chassis
}

class Car: Automobile {
    private var _chassis: Chassis

    init () {
        _chassis = Chassis()
    }

    func chassis() -> Chassis {
        return _chassis
    }
}

class RaceCar: Car {
    private var _racingChassis: RacingChassis

    override init () {
        _racingChassis = RacingChassis()
        super.init()
    }

    override func chassis() -> Chassis {
        return _racingChassis
    }
}

Değişkenler kullanarak bir API tasarlamanın neden bozulduğuna dair başka bir örnek, bir protokoldeki değişkenlere sahip olduğunuz zamandır. Tüm protokol işlevlerini bir uzantıya bölmek isterseniz, depolanan özelliklerin uzantılara yerleştirilememesi ve sınıfta tanımlanması gerekmesi dışında (bunu derlemek için kodun açıklamasını kaldırmanız gerekir) AdaptableViewController sınıfı ve mod değişkenini uzantıdan kaldırın):

protocol Adaptable {
    var mode: Int { get set }
    func adapt()
}

class AdaptableViewController: UIViewController {
    // var mode = 0
}

extension AdaptableViewController: Adaptable {

    var mode = 0 // compiler error

    func adapt() {
        //TODO: add adapt code
    }
}

Yukarıdaki kodda şu derleyici hatası olacaktır: "Uzantıların depolanmış özellikleri olmayabilir". Protokoldeki her şeyin işlevler kullanılarak uzantıda ayrılması için yukarıdaki örneği nasıl yeniden yazabileceğiniz aşağıda açıklanmıştır:

protocol Adaptable {
    func mode() -> Int
    func adapt()
}

class AdaptableViewController: UIViewController {
}

extension AdaptableViewController: Adaptable {
    func mode() -> Int {
        return 0
    }
    func adapt() {
        // adapt code
    }
}

1

Bunu jenerik kullanarak başarabilirsiniz:

class Descriptor {
    let var1 = "a"
}

class OtherDescriptor: Descriptor {
    let var2 = "b"
}

class Asset<D: Descriptor> {
    let descriptor: D

    init(withDescriptor descriptor: D) {
        self.descriptor = descriptor
    }

    func printInfo() {
        print(descriptor.var1)
    }
}

class OtherAsset<D: OtherDescriptor>: Asset<D> {
    override func printInfo() {
        print(descriptor.var1, descriptor.var2)
    }
}

let asset = Asset(withDescriptor: Descriptor())
asset.printInfo() // a

let otherAsset = OtherAsset(withDescriptor: OtherDescriptor())
otherAsset.printInfo() // a b

Bu yaklaşımla, zorla açmadan% 100 güvenli kod elde edeceksiniz.

Ancak bu bir çeşit hack'tir ve birkaç özelliği yeniden tanımlamanız gerekirse, sınıf bildirimleri tam bir karmaşa gibi görünecektir. Bu yüzden bu yaklaşıma dikkat edin.


1

Özelliği nasıl kullanmayı planladığınıza bağlı olarak, bunu yapmanın en basit yolu, alt sınıfınız için isteğe bağlı bir tür kullanmak didSet {}ve süper için yöntemi geçersiz kılmaktır:

class Chassis { }
class RacingChassis: Chassis { }

class Car {
    // Declare this an optional type, and do your 
    // due diligence to check that it's initialized
    // where applicable
    var chassis: Chassis?
}
class RaceCar: Car {
    // The subclass is naturally an optional too
    var racingChassis: RacingChassis?
    override var chassis: Chassis {
        didSet {
            // using an optional, we try to set the type
            racingChassis = chassis as? RacingChassis
        }
    }
}

Açıkçası, sınıfların bu şekilde başlatılabilmesini sağlamak için biraz zaman harcamanız gerekir, ancak özellikleri isteğe bağlı olarak ayarlayarak, dökümün artık çalışmadığı durumlara karşı kendinizi korursunuz.


0

Başka bir RacingChassis değişkenini kolayca oluşturabilirsiniz.

class Chassis {}
class RacingChassis : Chassis {}
class Car {
    let chassis: Chassis
    init(){
        chassis = Chassis()
}}

class RaceCar: Car {
let raceChassis: RacingChassis
init(){
        raceChassis = RacingChassis()
}}

Bu eserler, ama Dash cevabı geçersiz kılarak bunu bir adım ileri sürer düşünüyorum chassisve bize ulaşmalarına olanak tanımaktadır / bizim set RacingChassisile örneğini chassisözelliği.
James

0

Bunu dene:

class Chassis {}
class RacingChassis : Chassis {}
class SuperChassis : RacingChassis {}

class Car {
    private var chassis: Chassis? = nil
    func getChassis() -> Chassis? {
        return chassis
    }

    func setChassis(chassis: Chassis) {
        self.chassis = chassis
    }
}

class RaceCar: Car {
    private var chassis: RacingChassis {
        get {
            return getChassis() as! RacingChassis
        }
        set {
            setChassis(chassis: newValue)
        }
    }

    override init() {
        super.init()

        chassis = RacingChassis()
    }
}

class SuperCar: RaceCar {
    private var chassis: SuperChassis {
        get {
            return getChassis() as! SuperChassis
        }
        set {
            setChassis(chassis: newValue)
        }
    }

    override init() {
        super.init()

        chassis = SuperChassis()
    }
}

0
class Chassis {}
class RacingChassis : Chassis {}

class Car {
    fileprivate let theChassis: Chassis
    var chassis: Chassis {
        get {
            return theChassis
        }
    }
    fileprivate init(_ chassis: Chassis) {
        theChassis = chassis
    }
    convenience init() {
        self.init(Chassis())
    }
}
class RaceCar: Car {
    override var chassis: RacingChassis {
        get {
            return theChassis as! RacingChassis
        }
    }
    init() {
        super.init(RacingChassis())
    }
}

0

Aşağıdaki, tek bir nesnenin temel ve türetilmiş sınıflarda kullanılmasına izin verir. Türetilmiş sınıfta, türetilmiş nesne özelliğini kullanın.

class Car {
    var chassis:Chassis?

    func inspect() {
        chassis?.checkForRust()
    }
}

class RaceCar: Car {
    var racingChassis: RacingChassis? {
        get {
            return chassis as? RacingChassis
        } 
    }

    override func inspect() {
        super.inspect()
        racingChassis?.tuneSuspension()
    }
}

0

Diğer yanıtların küçük bir varyasyonu, ancak bazı güzel faydaları ile daha basit ve daha güvenli.

class Chassis {}
class RacingChassis : Chassis {}

class Car {
    let chassis = Chassis()
}
class RaceCar: Car {
    var racingChassis: RacingChassis? {
        get {
            return chassis as? RacingChassis
        }
    }
}

Faydaları arasında şasinin ne olması gerektiğine dair herhangi bir kısıtlama olmaması (var, izin ver, isteğe bağlı, vb.) Ve RaceCar'ı alt sınıflara ayırmanın kolay olması yer alır. RaceCar'ın alt sınıfları, şasi (veya yarış şasisi) için kendi hesaplanmış değerine sahip olabilir.


-2

imgview gibi farklı adlandırma kurallarına sahip yeni görüntü görüntüleme özelliğini ayarlayın, çünkü imageView zaten kendi özelliğidir ve 2 güçlü özellik atayamayız.

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.