Swift에서 다른 유형의 수퍼 클래스 속성 재정의


129

Swift에서 누군가가 원래 속성에서 하위 클래스로 분류 된 다른 객체를 사용하여 수퍼 클래스의 속성을 재정의하는 방법을 설명 할 수 있습니까?

이 간단한 예를 보자.

class Chassis {}
class RacingChassis : Chassis {}

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

오류가 발생합니다.

Cannot override with a stored property 'chassis'

대신 'var'로 섀시를 사용하면 오류가 발생합니다.

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

가이드의 "속성 재정의"에서 찾을 수있는 유일한 것은 getter 및 setter를 재정의해야한다는 것입니다.이 속성은 속성 값 ( 'var'인 경우)을 변경하는 데 효과적 일 수 있지만 속성 클래스 변경은 어떻습니까? ?

답변:


115

Swift에서는 변수 또는 속성의 클래스 유형을 변경할 수 없습니다. 대신 새 클래스 유형을 처리하는 서브 클래스에 추가 변수를 작성할 수 있습니다.

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 구문으로 속성을 선언 할 수없고 하위 클래스에서 var로 속성을 재정의 할 수 없으며 그 반대도 마찬가지입니다. 따라서이 경우 속성은 서브 클래스와 일치하도록 슈퍼 클래스에서 'var'로 선언해야합니다 (위의 스 니펫에 표시됨). 수퍼 클래스에서 소스 코드를 변경할 수 없다면, 섀시를 변경해야 할 때마다 현재 RaceCar를 파괴하고 새로운 RaceCar를 만드는 것이 가장 좋습니다.


1
죄송합니다. 내 댓글을 삭제 한 후 답장을 보았습니다. 내 실제 경우에 내 슈퍼 클래스는 strong속성 이있는 Objective-C 클래스 이고이를 재정의하려는 중에 오류 가 발생했기 때문에 문제가 있었지만 "내재적으로 래핑되지 않은 선택적"( chassis!)으로 변환되는 것을 놓친 것 같습니다 . 스위프트이므로 override var chassis : Chassis!수정하십시오.
제임스

4
이것은 더 이상 Xcode 6.1의 Swift 1.1에서 작동하지 않습니다. 오류를 생성합니다 : " 'var'게터로 불변의 'let'속성 'chassis'를 무시할 수 없습니다." 더 나은 솔루션에 대한 아이디어가 있습니까?
Darrarski

1
Xcode 6.3 베타 버전의 Swift 1.2에서도 더 이상 작동하지 않습니다. 공
변형

10
이것은 정말 절름발이이며 스위프트의 실패입니다. RacingChassis 섀시 이기 때문에 컴파일러는 하위 클래스에서 속성 클래스를 세분화 할 수있는 문제가 없어야합니다. 많은 언어가 이것을 허용하며 그것을 지원하지 않으면 이와 같은 추악한 해결 방법이 생깁니다. 공격하지 않습니다. : /
devios1

1
선택 사항은 변수가 더 이상 예상 값을 리턴하지 않는 함수에 의해 제공 되었기 때문에 존재하지 않을 수 있음을 제안하는 수단을 제공합니다. 함수가 재정의 된 경우 이러한 경우가 발생할 수 있습니다. 자세한 내용은 내 대답을 살펴보십시오.

10

이것은 작동하는 것 같습니다

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
이것은 클래스 let에서 와 같이 섀시 속성이 정의되어 Car변경이 불가능한 것처럼 작동합니다 . 클래스 의 chassis속성 만 변경할 수 있습니다 RaceCar.
David Arve 2016 년

4
Swift 2.0에서는 작동하지 않습니다. "오류 : 불변의 'let'속성 'chassis'를 'var'의 getter로 재정의 할 수 없습니다"
mohamede1945

이것은 swift 4.0에서도 작동하지 않습니다. layground 실행 실패 : 오류 : MyPlaygrounds.playground : 14 : 15 : 오류 : 'var'재정의 var 게터로 불변의 'let'속성 'chassis'를 재정의 할 수 없습니다. var 섀시 : RacingChassis {^ MyPlaygrounds.playground : 11 : 6 : 참고 : 재정의 속성에 대한 시도는 여기에하자 섀시 = 섀시 () ^
카림

7

이 시도:

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

그때:

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

http://www.mylonly.com/14957025459875.html의 세부 사항


아 깔끔하고 깨끗하다
Kumar Rathore Inder

3

제공된 솔루션 대시는 수퍼 클래스를 var가 아닌 let 키워드로 선언해야한다는 점을 제외하고는 잘 작동합니다. 다음은 가능하지만 권장되지 않는 솔루션입니다!

아래 솔루션은 Xcode 6.2, SWIFT 1.1 (모든 클래스가 다른 빠른 파일에있는 경우)로 컴파일되지만 IT는 예외없는 행동 (특히 비 선택적 유형을 사용할 때 충돌 포함)으로 이어질 수 있으므로 피해야합니다. 참고 :이 XCODE 6.3 베타 3, SWIFT 1.2에서는 작동하지 않습니다

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에서는 작동하지 않습니다. "섀시 유형의 변경 가능한 속성 '섀시'를 무시할 수 없습니까? ' 공변량 유형이 'RacingChassis?'인 "
mohamede1945

2

이론적으로는 이런 식으로 할 수 있습니다 ...

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

이것은 Xcode 놀이터에서 완벽하게 작동합니다.

그러나 실제 프로젝트에서 시도하면 컴파일러 오류가 나타납니다.

선언 'view'는 둘 이상의 수퍼 클래스 선언을 재정의 할 수 없습니다.

지금은 Xcode 6.0 GM 만 확인했습니다.

불행히도 Apple이이 문제를 해결할 때까지 기다려야합니다.

버그 보고서도 제출했습니다. 18518795


이것은 흥미로운 해결책이며 6.1에서 작동합니다.
Chris Conover

1

함수 대신 변수를 사용하여 API를 디자인하는 데 문제가 있고 계산 속성을 사용하는 것이 해결 방법처럼 느껴지는 많은 이유를 보았습니다. 인스턴스 변수를 캡슐화해야하는 좋은 이유가 있습니다. 여기에 자동차가 준수하는 프로토콜 자동차를 만들었습니다. 이 프로토콜에는 Chassis 객체를 반환하는 접근 자 메서드가 있습니다. Car가이를 준수하므로 RaceCar 서브 클래스는이를 무시하고 다른 Chassis 서브 클래스를 반환 할 수 있습니다. 이를 통해 Car 클래스는 인터페이스 (Automobile)로 프로그래밍 할 수 있으며 RacingChassis에 대해 알고있는 RaceCar 클래스는 _racingChassis 변수에 직접 액세스 할 수 있습니다.

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

변수를 사용하여 API를 설계하는 이유의 또 다른 예는 프로토콜에 변수가있는 경우입니다. 저장된 속성을 확장에 배치 할 수없고 클래스에 정의해야한다는 점을 제외하고 모든 프로토콜 기능을 확장으로 나누려면 (컴파일하기 위해 코드를 주석 해제해야합니다.) AdaptableViewController 클래스 및 확장에서 모드 변수 제거) :

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

위의 코드는 "컴파일러에 저장된 속성이 없을 수 있습니다"라는 컴파일러 오류가 발생합니다. 다음 예제에서 함수를 대신 사용하여 프로토콜의 모든 것을 확장에서 분리 할 수 ​​있도록 위의 예제를 다시 작성할 수 있습니다.

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

class AdaptableViewController: UIViewController {
}

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

1

제네릭을 사용하면 얻을 수 있습니다.

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

이 방법을 사용하면 강제로 풀지 않고 100 % 유형 안전 코드를 사용할 수 있습니다.

그러나 이것은 일종의 해킹이므로 클래스 선언보다 여러 속성을 다시 정의 해야하는 경우 전체 선언처럼 보일 것입니다. 따라서이 접근 방식에주의하십시오.


1

속성 사용 계획에 따라 가장 간단한 방법은 서브 클래스에 선택적 유형을 사용 didSet {}하고 수퍼 클래스의 메소드를 대체하는 것입니다.

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

클래스를 이런 식으로 초기화 할 수 있는지 확인하는 데 시간을 투자해야하지만 속성을 선택적으로 설정하면 캐스팅이 더 이상 작동하지 않는 상황으로부터 자신을 보호 할 수 있습니다.


0

RacingChassis의 다른 변수를 간단하게 만들 수 있습니다.

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

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

이것은 작동하지만 Dash의 대답은 속성을 사용하여 인스턴스 chassis를 가져 오거나 설정하도록 허용 하여이 단계를 한 단계 더 발전 RacingChassis시킵니다 chassis.
제임스

0

이 시도:

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

다음은 단일 클래스를 기본 클래스와 파생 클래스에서 사용할 수 있도록합니다. 파생 클래스에서 파생 개체 속성을 사용하십시오.

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

다른 답변의 약간의 변형이지만 몇 가지 좋은 이점이있는 더 간단하고 안전합니다.

class Chassis {}
class RacingChassis : Chassis {}

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

이점은 섀시의 종류 (var, let, optional 등)에 대한 제한이 없으며 RaceCar를 서브 클래 싱하기 쉽다는 것입니다. 그러면 RaceCar의 서브 클래스는 섀시 (또는 racingChassis)에 대해 자체 계산 된 값을 가질 수 있습니다.


-2

imageView는 이미 자체 속성이므로 2 개의 강력한 속성을 할당 할 수 없으므로 imgview와 같은 다른 명명 규칙으로 새 imageview 속성을 설정하면됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.