기존 답변에서 누락 된 Swift 관련 정보 에는 두 가지가 절대적으로 중요 합니다.
- 프로토콜이 이니셜 라이저를 필수 메소드로 지정하는 경우 해당 이니셜 라이저는 Swift의
required
키워드를 사용하여 표시해야합니다 .
- 스위프트에는
init
메소드 와 관련된 특별한 상속 규칙이 있습니다.
TL; DR은 이것이다 :
이니셜 라이저를 구현하면 더 이상 수퍼 클래스의 지정된 이니셜 라이저를 상속하지 않습니다.
상속받을 유일한 이니셜 라이저는 무시한 지정된 이니셜 라이저를 가리키는 수퍼 클래스 편의 이니셜 라이저입니다.
긴 버전을 준비 했습니까?
스위프트에는 init
메소드 와 관련된 특별한 상속 규칙이 있습니다.
나는 이것이 두 가지 요점 중 두 번째라는 것을 알고 있지만 첫 번째 요점 required
을 이해할 수 없거나이 요점을 이해할 때까지 키워드가 존재하는 이유를 알 수 없습니다 . 일단 우리가이 점을 이해하면, 다른 점은 꽤 분명해집니다.
이 답변의이 섹션에서 다루는 모든 정보는 여기에있는 Apple의 설명서를 참조하십시오 .
Apple 문서에서 :
Objective-C의 서브 클래스와 달리 Swift 서브 클래스는 기본적으로 수퍼 클래스 이니셜 라이저를 상속하지 않습니다. Swift의 접근 방식은 수퍼 클래스의 간단한 이니셜 라이저가보다 특수한 서브 클래스에 의해 상속되고 완전히 또는 올바르게 초기화되지 않은 서브 클래스의 새 인스턴스를 작성하는 데 사용되는 상황을 방지합니다.
강조합니다.
따라서 Apple 문서에서 바로 Swift 서브 클래스가 항상 슈퍼 클래스의 init
메소드를 상속하지는 않습니다 .
그렇다면 언제 슈퍼 클래스에서 상속 받습니까?
서브 클래스 init
가 부모 로부터 메소드를 상속하는시기를 정의하는 두 가지 규칙이 있습니다 . Apple 문서에서 :
규칙 1
서브 클래스가 지정된 이니셜 라이저를 정의하지 않으면 모든 수퍼 클래스 지정 이니셜 라이저를 자동으로 상속합니다.
규칙 2
서브 클래스가 규칙 1에 따라 상속 받거나 정의의 일부로 사용자 정의 구현을 제공하여 모든 수퍼 클래스 지정 이니셜 라이저의 구현을 제공하는 경우 모든 수퍼 클래스 편의 이니셜 라이저를 자동으로 상속합니다.
때문에 규칙이이 대화에 특히 관련이없는 SKSpriteNode
의이 init(coder: NSCoder)
편리한 메소드 수 없을 수도 있습니다.
따라서 InfoBar
클래스는 required
추가 할 때까지 바로 초기화 프로그램 을 상속 했습니다 init(team: Team, size: CGSize)
.
이 제공되지 않은 것 인 경우에 init
방법을 대신하여 만든 InfoBar
'옵션 추가 s의 속성 또는 기본 값을 제공, 당신은 여전히 상속 된 것 SKSpriteNode
'이야 init(coder: NSCoder)
. 그러나 우리 자신의 사용자 정의 이니셜 라이저를 추가했을 때, 우리는 슈퍼 클래스의 지정된 이니셜 라이저 (및 구현 한 이니셜 라이저를 가리 키지 않는 편의 이니셜 라이저)의 상속을 중단했습니다 .
그래서 간단한 예로, 나는 이것을 제시한다.
class Foo {
var foo: String
init(foo: String) {
self.foo = foo
}
}
class Bar: Foo {
var bar: String
init(foo: String, bar: String) {
self.bar = bar
super.init(foo: foo)
}
}
let x = Bar(foo: "Foo")
다음과 같은 오류가 발생합니다.
호출중인 'bar'매개 변수에 대한 인수가 누락되었습니다.
이것이 Objective-C라면 상속에 아무런 문제가 없습니다. Objective-C에서 Bar
with initWithFoo:
를 초기화 하면 self.bar
속성은 간단하게 nil
됩니다. 아마 잘은 아니지만, 완벽의 유효한 객체가 될위한 상태. 그건 없습니다 . 스위프트의 객체가 될하기위한 완벽하게 유효한 상태 self.bar
선택하지 않고 수 없습니다 nil
.
다시 한번, 우리가 이니셜 라이저를 상속받는 유일한 방법은 우리 자신의 것을 제공하지 않는 것입니다. 우리는 삭제하여 상속하려고 그렇다면 Bar
'들 init(foo: String, bar: String)
과 같은 :
class Bar: Foo {
var bar: String
}
이제 우리는 상속 (다양한)으로 돌아 왔지만 컴파일되지는 않습니다 ... 오류 메시지는 왜 슈퍼 클래스 init
메소드를 상속하지 않는지를 정확하게 설명 합니다.
문제 : 클래스 'Bar'에 이니셜 라이저가 없습니다.
Fix-It : 이니셜 라이저가없는 저장된 속성 'bar'는 합성 된 이니셜 라이저를 방지합니다.
서브 클래스에 저장된 속성을 추가했다면 서브 클래스의 저장된 속성에 대해 알 수 없었던 수퍼 클래스 이니셜 라이저를 사용하여 서브 클래스의 유효한 인스턴스를 생성하는 스위프트 방법은 없습니다.
좋아, 왜 구현 init(coder: NSCoder)
해야합니까? 왜 그렇 required
습니까?
Swift의 init
메소드는 특별한 상속 규칙 세트에 의해 작동 할 수 있지만 프로토콜 적합성은 여전히 체인 아래로 상속됩니다. 부모 클래스가 프로토콜을 준수하는 경우 해당 서브 클래스가 해당 프로토콜을 준수해야합니다.
대부분의 프로토콜에는 Swift의 특수 상속 규칙에 따라 재생되지 않는 메소드 만 필요하므로 일반적으로 문제가되지 않으므로 프로토콜을 준수하는 클래스에서 상속하는 경우 모든 프로토콜을 상속합니다. 클래스가 프로토콜 적합성을 만족시킬 수있는 메소드 또는 프로퍼티
그러나 Swift의 init
메소드는 특별한 규칙 세트를 사용하며 항상 상속되는 것은 아닙니다. 이 때문에 특수 init
메소드 (예 :)가 필요한 프로토콜을 따르는 클래스는 NSCoding
해당 init
메소드를로 표시해야합니다 required
.
이 예제를 고려하십시오.
protocol InitProtocol {
init(foo: Int)
}
class ConformingClass: InitProtocol {
var foo: Int
init(foo: Int) {
self.foo = foo
}
}
컴파일되지 않습니다. 다음과 같은 경고가 생성됩니다.
문제 : 이니셜 라이저 요구 사항 'init (foo :)'는 최종 클래스가 아닌 'ConformingClass'의 '필수'이니셜 라이저 만 충족 할 수 있습니다.
Fix-It : 인서트 필요
init(foo: Int)
이니셜 라이저를 필수 로 만들고 싶습니다 . 클래스를 만들어서 행복하게 만들 수도 있습니다 final
(클래스를 상속받을 수 없음을 의미).
하위 클래스를 만들면 어떻게됩니까? 이 시점에서 하위 클래스를 작성하면 괜찮습니다. 그래도 초기화 프로그램을 추가하면 갑자기 더 이상 상속되지 않습니다 init(foo:)
. 이제 더 이상에 따르지 않기 때문에 문제가 InitProtocol
됩니다. 프로토콜을 따르는 클래스에서 서브 클래스를 만들 수 없으며 갑자기 해당 프로토콜을 따르지 않기로 결정합니다. 프로토콜 준수를 상속했지만 Swift가 init
메소드 상속 과 작동하는 방식으로 인해 해당 프로토콜을 준수하는 데 필요한 일부를 상속하지 않았으므로 구현해야합니다.
좋아,이 모든 것이 말이된다. 그러나 왜 더 유용한 오류 메시지를 얻을 수 없습니까?
클래스가 더 이상 상속 된 NSCoding
프로토콜을 따르지 않고 이를 수정해야 한다고 지정한 경우 오류 메시지가 더 명확하거나 더 좋을 수 있습니다 init(coder: NSCoder)
. 확실한.
그러나 Xcode는 단순히 메시지를 생성 할 수 없습니다. 실제로 필요한 메소드를 구현하거나 상속하지 않는 데 실제로 실제 문제가되는 것은 아닙니다. 프로토콜 준수 외에 init
메소드 를 작성 required
해야하는 다른 이유가 적어도 하나 있습니다. 이것이 팩토리 메소드입니다.
올바른 팩토리 메소드를 작성하려면 리턴 유형을 Self
(Swift의 Objective-C와 동일) 로 지정해야합니다 instanceType
. 그러나 이렇게하려면 실제로 required
초기화 메소드 를 사용해야합니다 .
class Box {
var size: CGSize
init(size: CGSize) {
self.size = size
}
class func factory() -> Self {
return self.init(size: CGSizeZero)
}
}
오류가 발생합니다.
메타 타입 값으로 'Self'클래스 유형의 오브젝트를 구성하려면 '필수'이니셜 라이저를 사용해야합니다
기본적으로 같은 문제입니다. 하위 클래스 Box
인 경우 하위 클래스는 클래스 메서드를 상속합니다 factory
. 그래서 우리는 전화 할 수있었습니다 SubclassedBox.factory()
. 그러나없이 required
키워드 on init(size:)
방법 Box
의 서브 클래스는 상속 보장 할 수 없습니다 self.init(size:)
가 factory
호출됩니다.
그래서 우리는 required
이와 같은 팩토리 메소드를 원한다면 그 메소드를 만들어야합니다. 즉 , 클래스가 이와 같은 메소드를 구현하면 required
초기화 메소드가 있고 여기에서 실행했던 것과 동일한 문제가 발생합니다. 와 NSCoding
프로토콜입니다.
궁극적으로 Swift의 이니셜 라이저가 약간 다른 상속 규칙 세트를 사용한다는 기본 이해로 요약됩니다. 즉, 슈퍼 클래스에서 이니셜 라이저를 상속받지 못할 수도 있습니다. 슈퍼 클래스 이니셜 라이저는 새로운 저장된 속성을 알 수없고 객체를 유효한 상태로 인스턴스화 할 수 없기 때문에 발생합니다. 그러나 여러 가지 이유로 수퍼 클래스는 이니셜 라이저를로 표시 할 수 있습니다 required
. 그렇게 할 때 실제로 required
메서드를 상속하는 매우 구체적인 시나리오 중 하나를 사용 하거나 직접 구현해야합니다.
여기서 중요한 점은 여기에 오류가 발생하면 클래스가 실제로 메소드를 구현하지 않는다는 것을 의미합니다.
Swift 서브 클래스가 항상 부모의 init
메소드를 상속하지는 않는다는 사실을 익히기위한 마지막 예제 일 수 있습니다 (이 문제를 완전히 이해하는 데 절대적으로 중요하다고 생각합니다).
class Foo {
init(a: Int, b: Int, c: Int) {
// do nothing
}
}
class Bar: Foo {
init(string: String) {
super.init(a: 0, b: 1, c: 2)
// do more nothing
}
}
let f = Foo(a: 0, b: 1, c: 2)
let b = Bar(a: 0, b: 1, c: 2)
컴파일에 실패했습니다.
오류 메시지는 약간 오해의 소지가 있습니다.
추가 인수 'b'호출
그러나 요점은 부모 클래스에서 메소드 를 상속하는 두 가지 특별한 경우 중 하나를 만족하지 않기 때문에 의 메소드를 Bar
상속하지 않는다는 것 입니다.Foo
init
init
이것이 Objective-C라면 init
Objective-C는 객체의 속성을 초기화하지 않는 것이 기쁘기 때문에 문제없이 상속받습니다 . Swift에서는 간단하지 않습니다. 유효하지 않은 상태를 가질 수 없으며 상속되는 수퍼 클래스 이니셜 라이저는 유효하지 않은 오브젝트 상태 만 초래할 수 있습니다.
init(collection:MPMediaItemCollection)
. 실제 미디어 항목 모음을 제공해야합니다. 그것이이 수업의 요점입니다. 이 클래스는 클래스없이 인스턴스화 할 수 없습니다. 컬렉션을 분석하고 12 개의 인스턴스 변수를 초기화합니다. 이것이 유일하고 지정된 이니셜 라이저가되는 요점입니다! 따라서init(coder:)
여기서 제공 할 의미가있는 (또는 의미없는) MPMediaItemCollection이 없습니다.fatalError
접근 방식 만 옳습니다.