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
답변
제공된 솔루션 대시는 수퍼 클래스를 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 % 유형 안전 코드를 사용할 수 있습니다.
그러나 이것은 일종의 해킹이므로 클래스 선언보다 여러 속성을 다시 정의 해야하는 경우 전체 선언처럼 보일 것입니다. 따라서이 접근 방식에주의하십시오.