Objective-C 에서는 class 이름 뒤에 을 붙여서 프로토콜을 준수하는 클래스 타입의 변수를 나타낼 수 있었습니다.

// Objective-C
@interface NSCandidateListTouchBarItem<CandidateType> : NSTouchBarItem
@property (nullable, weak) NSView <NSTextInputClient> *client;
@end

위 코드는 client 라는 프로퍼티가 NSView 이며, NSTextInputClient 라는 프로토콜을 준수해야 한다는 의미입니다.

이 코드를 Swift 3 로 바꿔보겠습니다.

// Swift 3
class NSCandidateListTouchBarItem<CandidateType: AnyObject> : NSTouchBarItem {
  var client: NSView?
}

보시다시피 client 가 NSView 라는 건 표시했지만, protocol 을 준수한다는 걸 표현할 방법이 없습니다.

Swift 4 에선 이 문제가 해결되었습니다.

// Swift 4
class NSCandidateListTouchBarItem<CandidateType: AnyObject> : NSTouchBarItem {
  var client: (NSView & NSTextInputClient)?
}

& 로 묶어서 class 와 protocol 을 동시에 표현할 수 있게 되었습니다.


몇 가지 주의 사항입니다.

  • 클래스와 여러가지 프로토콜을 동시에 사용할 수 있습니다.
  • 나열하는 순서는 상관 없습니다. (protocol & class)
  • struct 와는 사용할 수 없습니다.
  • typealias 를 사용해서 하나의 타입으로 만들 수 있습니다.
class NSCandidateListTouchBarItem<CandidateType: AnyObject> : NSTouchBarItem {
  typealias TextInputView = NSView & NSTextInputClient
  var client: TextInputView?
}

class 를 선언할 때 이 방법을 쓰면 안 됩니다. 기존과 똑같이 사용합니다.

class TextInputView: NSView, NSTextInputClient {
}

Swift Evolution 사이트의 예제를 확인해 보세요.

protocol P {}
struct S : P {}
class C : P {}
class D { }
let t: AnyObject & P = S() // Compiler error: S is not of class type
let u: AnyObject & P = C() // Compiles successfully
let v: P & AnyObject = C() // Compiles successfully
let w: P & AnyObject = D() // Compiler error: class D does not conform to protocol P
protocol P {}
struct S {}
class C {}
class D : P {}
class E : C, P {}
let u: S & P // Compiler error: S is not of class type
let v: C & P = D() // Compiler error: D is not a subtype of C
let w: C & P = E() // Compiles successfully
protocol P {}
class C {}
class D : C, P { }
let u: AnyObject & C & P = D() // Okay: D is a subclass of C and conforms to P 
let v: C & P = u               // Okay: C & P is equivalent to AnyObject & C & P
let w: AnyObject & C & P = v   // Okay: AnyObject & C & P is equivalent to C & P
protocol P {}
class C {}
class D : C { }
class E : C { }
class F : D, P { }
let t: C & D & P = F() // Okay: F is a subclass of D and conforms to P
let u: D & P = t       // Okay: D & P is equivalent to C & D & P
let v: C & D & P = u   // Okay: C & D & P is equivalent to D & P
let w: D & E & P       // Compiler error: D is not a subclass of E or vice-versa
class C {}
class D : C {}
class E {}
protocol P1 {}
protocol P2 {}
typealias TA1 = AnyObject & P1
typealias TA2 = AnyObject & P2
typealias TA3 = C & P2
typealias TA4 = D & P2
typealias TA5 = E & P2

typealias TA5 = TA1 & TA2
// Expansion: typealias TA5 = AnyObject & P1 & AnyObject & P2
// Normalization: typealias TA5 = AnyObject & P1 & P2 
// TA5 is valid

typealias TA6 = TA1 & TA3
// Expansion: typealias TA6 = AnyObject & P1 & C & P2 
// Normalization (AnyObject < C): typealias TA6 = C & P1 & P2 
// TA6 is valid

typealias TA7 = TA3 & TA4
// Expansion: typealias TA7 = C & P2 & D & P2
// Normalization (C < D): typealias TA7 = D & P2
// TA7 is valid

typealias TA8 = TA4 & TA5
// Expansion: typealias TA8 = D & P2 & E & P2
// Normalization: typealias TA8 = D & E & P2
// TA8 is invalid because the D and E constraints are incompatible