[swift] 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 let newRacingChassis = newValue as? RacingChassis {
                racingChassis = newRacingChassis
            } 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
    }
}


답변

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

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

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