Programing

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

crosscheck 2020. 7. 16. 08:14
반응형

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


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'인 경우)을 변경하는 데 효과적 일 수 있지만 속성 클래스 변경은 어떻습니까? ?


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 newValue is RacingChassis {
                racingChassis = newValue as RacingChassis
            } else {
                println("incorrect chassis type for racecar")
            }
        }
    }
}

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


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

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

이 시도:

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의 세부 사항


제공된 솔루션 대시는 수퍼 클래스를 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
        }
    }
}

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

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


함수 대신 변수를 사용하여 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
    }
}

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

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

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

이 시도:

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

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


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

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 % 유형 안전 코드를 사용할 수 있습니다.

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


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

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

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

속성 사용 계획에 따라 가장 간단한 방법은 서브 클래스에 선택적 유형을 사용 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
        }
    }
}

Obviously you need to spend some time checking to ensure the classes can be initialized this way, but by setting the properties to optional, you protect yourself against situations where casting no longer works.

참고URL : https://stackoverflow.com/questions/24094158/overriding-superclass-property-with-different-type-in-swift

반응형