[swift] Swift (UI)에서`some` 키워드는 무엇입니까?
새로운 SwiftUI 튜토리얼 에는 다음과 같은 코드가 있습니다.
struct ContentView: View {
var body: some View {
Text("Hello World")
}
}
두 번째 줄인 단어 some
는 사이트에서 키워드 인 것처럼 강조 표시됩니다.
Swift 5.1에는 some
키워드 가없는 것으로 보이며 단어 some
가 일반적으로 수행되는 위치로 이동하기 때문에 단어 가 다른 작업을 수행 할 수없는 것으로 보입니다 . 발표되지 않은 새로운 Swift 버전이 있습니까? 내가 알지 못하는 방식으로 유형에 사용되는 함수입니까?
키워드 some
는 무엇을합니까?
답변
some View
이다 불투명 결과 유형 에 의해 도입으로 SE-0244 과 “반대”일반적인 자리 것으로 생각할 수 있습니다 엑스 코드 11. 스위프트 5.1에서 사용할 수는.
발신자가 만족하는 일반 일반 자리 표시 자와 달리 :
protocol P {}
struct S1 : P {}
struct S2 : P {}
func foo<T : P>(_ x: T) {}
foo(S1()) // Caller chooses T == S1.
foo(S2()) // Caller chooses T == S2.
불투명 한 결과 유형은 구현 에서 충족되는 암시 적 일반 자리 표시 자 이므로 다음과 같이 생각할 수 있습니다.
func bar() -> some P {
return S1() // Implementation chooses S1 for the opaque result.
}
다음과 같이 보입니다 :
func bar() -> <Output : P> Output {
return S1() // Implementation chooses Output == S1.
}
실제로이 기능의 최종 목표는보다 명확한 형식으로 리버스 제네릭을 허용하는 것 -> <T : Collection> T where T.Element == Int
입니다. 예를 들어 제약 조건을 추가 할 수도 있습니다 . 자세한 내용은이 게시물을 참조하십시오 .
이것을 제거하는 가장 중요한 것은 함수 리턴 some P
은 에 맞는 특정 단일 콘크리트 유형 의 값을 리턴하는 것 P
입니다. 함수 내에서 다른 적합한 유형을 반환하려고하면 컴파일러 오류가 발생합니다.
// error: Function declares an opaque return type, but the return
// statements in its body do not have matching underlying types.
func bar(_ x: Int) -> some P {
if x > 10 {
return S1()
} else {
return S2()
}
}
암시 적 일반 자리 표시자는 여러 유형으로 충족 될 수 없습니다.
이것은 반환하는 함수 대조적이다 P
나타내는 데 사용될 수 있고, 둘 다 S1
및 S2
그것의 임의 나타내므로 P
따르는 값 :
func baz(_ x: Int) -> P {
if x > 10 {
return S1()
} else {
return S2()
}
}
그렇다면 불투명 한 결과 유형 -> some P
은 프로토콜 반환 유형보다 어떤 이점이 -> P
있습니까?
1. PAT와 함께 불투명 한 결과 유형을 사용할 수 있습니다
프로토콜의 현재 주요 제한 사항은 PAT (관련 유형의 프로토콜)를 실제 유형으로 사용할 수 없다는 것입니다. 이는 향후 버전의 언어에서 해제 될 수있는 제한 사항이지만 불투명 한 결과 유형은 사실상 일반적인 자리 표시 자이므로 오늘날 PAT와 함께 사용할 수 있습니다.
즉, 다음과 같은 작업을 수행 할 수 있습니다.
func giveMeACollection() -> some Collection {
return [1, 2, 3]
}
let collection = giveMeACollection()
print(collection.count) // 3
2. 불투명 한 결과 유형은 동일성
불투명 한 결과 유형으로 인해 단일 콘크리트 유형이 반환되므로 컴파일러는 동일한 함수에 대한 두 번의 호출이 동일한 유형의 두 값을 반환해야한다는 것을 알고 있습니다.
즉, 다음과 같은 작업을 수행 할 수 있습니다.
// foo() -> <Output : Equatable> Output {
func foo() -> some Equatable {
return 5 // The opaque result type is inferred to be Int.
}
let x = foo()
let y = foo()
print(x == y) // Legal both x and y have the return type of foo.
컴파일러가이를 알고 x
있고 y
구체적인 유형이 같기 때문에 이것은 합법적 입니다. 이것은 ==
두 가지 유형의 매개 변수 가있는 중요한 요구 사항입니다 Self
.
protocol Equatable {
static func == (lhs: Self, rhs: Self) -> Bool
}
즉, 콘크리트 규격 유형과 동일한 유형의 두 값이 필요합니다. Equatable
유형으로 사용 가능 하더라도 두 개의 임의의 Equatable
일치 값을 서로 비교할 수 없습니다 .
func foo(_ x: Int) -> Equatable { // Assume this is legal.
if x > 10 {
return 0
} else {
return "hello world"
}
}
let x = foo(20)
let y = foo(5)
print(x == y) // Illegal.
컴파일러는 두 개의 임의 Equatable
값이 동일한 기본 콘크리트 유형을 가지고 있음을 증명할 수 없습니다 .
비슷한 방식으로, 또 다른 불투명 타입 반환 함수를 도입했다면 :
// foo() -> <Output1 : Equatable> Output1 {
func foo() -> some Equatable {
return 5 // The opaque result type is inferred to be Int.
}
// bar() -> <Output2 : Equatable> Output2 {
func bar() -> some Equatable {
return "" // The opaque result type is inferred to be String.
}
let x = foo()
let y = bar()
print(x == y) // Illegal, the return type of foo != return type of bar.
모두 있지만 있기 때문에 예를 들어 불법이됩니다 foo
및 bar
반환 some Equatable
, 자신의 일반적인 자리를 “반대” Output1
와 Output2
다른 유형에 의해 만족 될 수있다.
3. 불투명 한 결과 유형은 일반 자리 표시 자로 구성됩니다.
일반 프로토콜 유형 값과 달리 불투명 한 결과 유형은 일반 일반 자리 표시 자와 함께 잘 구성됩니다.
protocol P {
var i: Int { get }
}
struct S : P {
var i: Int
}
func makeP() -> some P { // Opaque result type inferred to be S.
return S(i: .random(in: 0 ..< 10))
}
func bar<T : P>(_ x: T, _ y: T) -> T {
return x.i < y.i ? x : y
}
let p1 = makeP()
let p2 = makeP()
print(bar(p1, p2)) // Legal, T is inferred to be the return type of makeP.
예를 들어, 두 값이 서로 다른 기본 콘크리트 유형을 가질 수 있기 makeP
때문에 방금 반환 된 경우 작동하지 않습니다 .P
P
struct T : P {
var i: Int
}
func makeP() -> P {
if .random() { // 50:50 chance of picking each branch.
return S(i: 0)
} else {
return T(i: 1)
}
}
let p1 = makeP()
let p2 = makeP()
print(bar(p1, p2)) // Illegal.
콘크리트 유형에 불투명 한 결과 유형을 사용하는 이유는 무엇입니까?
이 시점에서 자신에게 생각할 수도 있습니다. 코드를 다음과 같이 작성하십시오.
func makeP() -> S {
return S(i: 0)
}
음, 불투명 한 결과 유형을 사용하면에서 S
제공하는 인터페이스 만 노출 하여 유형 을 구현 세부 사항으로 만들 수 P
있으므로 함수에 의존하는 코드를 중단하지 않고 콘크리트 유형을 나중에 줄 아래로 유연하게 변경할 수 있습니다.
예를 들어 다음을 교체 할 수 있습니다.
func makeP() -> some P {
return S(i: 0)
}
와:
func makeP() -> some P {
return T(i: 1)
}
호출하는 코드를 깨지 않고 makeP()
.
답변
다른 답변은 새 some
키워드 의 기술적 측면을 잘 설명 하지만이 답변은 이유 를 쉽게 설명하려고합니다 .
프로토콜 동물이 있고 두 동물이 형제인지 비교하고 싶습니다.
protocol Animal {
func isSibling(_ animal: Self) -> Bool
}
이런 식으로 두 동물이 같은 유형 의 동물 인 경우 두 동물이 형제인지 비교하는 것이 합리적 입니다.
이제 참조 용으로 동물의 예를 만들어 보겠습니다.
class Dog: Animal {
func isSibling(_ animal: Dog) -> Bool {
return true // doesn't really matter implementation of this
}
}
없는 길 some T
이제 ‘가족’에서 동물을 반환하는 함수가 있다고 가정 해 봅시다.
func animalFromAnimalFamily() -> Animal {
return myDog // myDog is just some random variable of type `Dog`
}
참고 :이 함수는 실제로 컴파일되지 않습니다. 프로토콜이 ‘Self’또는 generics를 사용하는 경우 ‘일부’기능이 추가되기 전에 프로토콜 유형을 반환 할 수 없기 때문 입니다. 그러나 당신이 할 수 있다고 가정 해 봅시다 … 이것은 myDog를 추상 유형의 동물로 업 캐스트하는 것입니다.
이제 문제를 해결하려고하면 문제가 발생합니다.
let animal1: Animal = animalFromAnimalFamily()
let animal2: Animal = animalFromAnimalFamily()
animal1.isSibling(animal2) // error
오류가 발생 합니다.
왜? 그 이유는 animal1.isSibling(animal2)
Swift에 전화를 걸 때 동물이 개인 지 고양이인지 알 수 없기 때문입니다. 스위프트는 알고있다, 지금까지대로 animal1
와 animal2
관련이없는 동물 종 수 있었다 . 우리는 다른 유형의 동물을 비교할 수 없기 때문에 (위 참조). 이 오류가 발생합니다
some T
이 문제를 해결 하는 방법
이전 함수를 다시 작성해 봅시다 :
func animalFromAnimalFamily() -> some Animal {
return myDog
}
let animal1 = animalFromAnimalFamily()
let animal2 = animalFromAnimalFamily()
animal1.isSibling(animal2)
animal1
하고 animal2
있습니다 하지 Animal
, 하지만 그들은 클래스가 구현하는 동물이다 .
이것이 당신이 지금 할 수 있습니다 당신이 호출 할 때입니다 animal1.isSibling(animal2)
, 스위프트가 알고 animal1
와 animal2
같은 유형입니다.
그래서 내가 생각하는 방식 :
some T
수 스위프트 의 어떤 구현 알고T
사용됩니다하지만 클래스의 사용자가하지 않습니다.
(자기 홍보 면책 조항) 이 새로운 기능에 대해 좀 더 깊이 있는 블로그 게시물 을 작성했습니다 (여기서와 동일한 예).
답변
Hamish의 답변 은 매우 훌륭하고 기술적 관점에서 질문에 답변합니다. some
Apple의 SwiftUI 튜토리얼 에서이 특정 위치에서 키워드 가 사용되는 이유와 따라야 하는 이유에 대해 몇 가지 생각을 추가하고 싶습니다 .
some
요구 사항이 아닙니다!
우선, 의 반환 유형을 불투명 유형으로 선언 할 필요 가 없습니다 body
. 를 사용하는 대신 항상 구체적 유형을 반환 할 수 있습니다 some View
.
struct ContentView: View {
var body: Text {
Text("Hello World")
}
}
이것도 컴파일됩니다. View
의 인터페이스 를 살펴보면 의 리턴 유형이 body
연관된 유형 임을 알 수 있습니다 .
public protocol View : _View {
/// The type of view representing the body of this view.
///
/// When you create a custom view, Swift infers this type from your
/// implementation of the required `body` property.
associatedtype Body : View
/// Declares the content and behavior of this view.
var body: Self.Body { get }
}
즉, 선택한 특정 유형으로 속성 에 주석을 달아이 유형 을 지정합니다 body
. 유일한 요구 사항은이 유형이 View
프로토콜 자체 를 구현해야한다는 것입니다.
예를 들어 구현 하는 특정 유형일 수 있습니다.View
Text
Image
Circle
- …
또는 을 구현 하는 불투명 한 유형 View
, 즉
some View
일반 뷰
스택 뷰를 또는 body
같은 리턴 유형 으로 사용하려고하면 문제가 발생합니다 .VStack
HStack
struct ContentView: View {
var body: VStack {
VStack {
Text("Hello World")
Image(systemName: "video.fill")
}
}
}
이것은 컴파일되지 않으며 오류가 발생합니다.
제네릭 형식 ‘VStack’에 대한 참조에는 <…>
SwiftUI의 스택 뷰 가 일반적인 유형 이기 때문입니다 ! 💡 (그리고 목록 및 다른 컨테이너보기 유형 에서도 마찬가지입니다 .)
( View
프로토콜을 준수하는 한 ) 모든 유형의 뷰를 여러 개 연결할 수 있기 때문에 의미가 있습니다 . VStack
위 신체 의 콘크리트 유형 은 실제로
VStack<TupleView<(Text, Image)>>
나중에 스택에 뷰를 추가하기로 결정하면 콘크리트 유형이 변경됩니다. 첫 번째 텍스트 다음에 두 번째 텍스트를 추가하면
VStack<TupleView<(Text, Text, Image)>>
텍스트와 이미지 사이에 스페이서를 추가하는 것처럼 미묘한 변화를 주더라도 스택의 유형이 변경됩니다.
VStack<TupleView<(Text, _ModifiedContent<Spacer, _FrameLayout>, Image)>>
내가 말할 수있는 건, 이다 그 이유 애플은 항상 사용에 자신의 튜토리얼에서 권장하는 이유 some View
, 모든 뷰가 같이 만족하는 가장 일반적인 불투명 타입 body
의 반환 유형입니다. 매번 리턴 유형을 수동으로 변경하지 않고 사용자 정의보기의 구현 / 레이아웃을 변경할 수 있습니다.
보충:
불투명 한 결과 유형을보다 직관적으로 이해하려면 최근 읽을 가치가있는 기사를 게시했습니다.
🔗 SwiftUI 에서이 “일부”는 무엇입니까?
답변
지금까지 모든 대답이 누락 된 것은 some
SwiftUI와 같은 DSL (도메인 특정 언어)이나 라이브러리 / 프레임 워크와 같은 사용자에게 유용하다는 것입니다. 사용자 (다른 프로그래머)는 자신과 다릅니다.
some
형식 제약 조건 대신 형식으로 사용할 수 있도록 일반 프로토콜을 래핑 할 수있는 경우를 제외하고는 일반 앱 코드에서는 절대 사용하지 않을 것입니다 . 어떤 some
일은 그것의 앞에 슈퍼 외관을 넣는 동안 컴파일러가 특정 유형의 일이 무엇인지에 대한 지식을 유지하도록하는 것입니다.
따라서 당신이 사용자의 SwiftUI에, 모든 당신이 알아야 할 그 무언가가입니다 some View
모든 종류의 속임수의 뒤에서 당신이 차폐되는에 갈 수 있지만. 이 객체는 실제로 매우 특정한 유형이지만 그 내용에 대해들을 필요는 없습니다. 그러나 프로토콜과 달리 본격적인 유형입니다. 프로토콜이 표시되는 곳은 특정 본격적인 유형의 외관 일뿐입니다.
SwiftUI의 향후 버전 some View
에서는 개발자가 해당 특정 객체의 기본 유형을 변경할 수 있습니다. 그러나 코드가 처음부터 기본 유형을 언급하지 않았기 때문에 코드가 손상되지 않습니다.
따라서 some
사실상 프로토콜을 수퍼 클래스처럼 만듭니다. 그것은이 거의 , 아니지만 꽤 실제 개체의 유형 (예를 들어, 프로토콜의 메소드 선언은 반환 할 수 없습니다 some
).
당신이 사용하려고한다면 그래서 some
아무것도, 경우 가장 가능성이 될 것입니다 당신이 다른 사람에 의해 사용을위한 DSL 또는 프레임 워크 / 라이브러리를 작성하고, 당신은 기본 유형의 세부 사항을 마스크 싶었다. 이를 통해 다른 사람들이 사용하기에 더 쉬운 코드를 만들 수 있으며 코드를 손상시키지 않고 구현 세부 사항을 변경할 수 있습니다.
그러나 코드의 한 영역을 코드의 다른 영역에 묻혀있는 구현 세부 정보에서 보호하는 방법으로 자체 코드에서 사용할 수도 있습니다.
답변
some
Swift 5.1 의 키워드 ( swift-evolution proposal )는 프로토콜과 함께 반환 유형으로 사용됩니다.
Xcode 11 릴리스 정보 는 다음과 같습니다.
함수는 정확한 반환 유형을 지정하는 대신 어떤 프로토콜을 준수하는지 선언하여 구체적인 반환 유형을 숨길 수 있습니다.
func makeACollection() -> some Collection { return [1, 2, 3] }
함수를 호출하는 코드는 프로토콜의 인터페이스를 사용할 수 있지만 기본 유형에 대한 가시성은 없습니다. ( SE- 0244, 40538331)
위의 예에서을 반환한다고 말할 필요는 없습니다 Array
. 그렇게하면 그냥 따르는 일반 유형을 반환 할 수도 Collection
있습니다.
발생할 수있는이 가능한 오류에 유의하십시오.
‘일부’리턴 유형은 iOS 13.0.0 이상에서만 사용 가능
즉, some
iOS 12 및 이전 버전 에서 사용하지 않으려면 가용성을 사용해야합니다 .
@available(iOS 13.0, *)
func makeACollection() -> some Collection {
...
}
답변
‘일부’는 불투명 한 유형을 의미합니다. SwiftUI에서 View는 프로토콜로 선언됩니다
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
public protocol View {
/// The type of view representing the body of this view.
///
/// When you create a custom view, Swift infers this type from your
/// implementation of the required `body` property.
associatedtype Body : View
/// Declares the content and behavior of this view.
var body: Self.Body { get }
}
뷰를 Struct로 만들면 View 프로토콜을 따르고 var body가 View Protocol을 확인하는 것을 반환한다고 알려줍니다. 구체적인 유형을 정의 할 필요가없는 일반적인 프로토콜 추상화와 같습니다.
답변
나는 (이 무엇인지 아주 기본적인 실제 예제를 통해이 대답하려고합니다 불투명 결과 유형 에 대한)
연관된 유형의 프로토콜과이를 구현하는 두 개의 구조체가 있다고 가정합니다.
protocol ProtocolWithAssociatedType {
associatedtype SomeType
}
struct First: ProtocolWithAssociatedType {
typealias SomeType = Int
}
struct Second: ProtocolWithAssociatedType {
typealias SomeType = String
}
Swift 5.1 이전에는 ProtocolWithAssociatedType can only be used as a generic constraint
오류로 인해 아래가 불법입니다 .
func create() -> ProtocolWithAssociatedType {
return First()
}
그러나 Swift 5.1에서는 이것이 좋습니다 ( some
추가).
func create() -> some ProtocolWithAssociatedType {
return First()
}
위의 SwiftUI에서 광범위하게 사용되는 실용적인 사용법입니다 some View
.
그러나 한 가지 중요한 제한 사항이 있습니다-컴파일 타임에 반환 유형을 알아야하므로 아래에서 다시 Function declares an opaque return type, but the return statements in its body do not have matching underlying types
오류가 발생 하지 않습니다 .
func create() -> some ProtocolWithAssociatedType {
if (1...2).randomElement() == 1 {
return First()
} else {
return Second()
}
}