Swift Language
프로토콜
수색…
소개
프로토콜은 객체 사용 방법을 지정하는 방법입니다. 프로토콜은 클래스, 구조체 또는 열거 형이 제공해야하는 속성 및 메서드 집합을 설명하지만 프로토콜은 구현에 대한 제한이 없습니다.
비고
스위프트 프로토콜은 준수 유형이 구현해야하는 요구 사항 모음입니다. 그런 다음 프로토콜은 예를 들어 어레이 및 일반적인 요구 사항과 같이 유형이 예상되는 대부분의 장소에서 사용될 수 있습니다.
프로토콜 멤버는 항상 전체 프로토콜과 동일한 액세스 한정자를 공유하므로 별도로 지정할 수 없습니다. 프로토콜은 위의 예제에 따라 getter 또는 setter 요구 사항을 사용하여 액세스를 제한 할 수 있습니다.
프로토콜에 대한 자세한 내용은 Swift 프로그래밍 언어를 참조하십시오.
Objective-C 프로토콜 은 Swift 프로토콜과 유사합니다.
프로토콜은 Java 인터페이스 와 비슷합니다.
프로토콜 기본 사항
프로토콜 정보
프로토콜은 프로토콜을 준수하는 Swift 객체 유형 (클래스, 구조체 또는 열거 형)에 필요한 초기화 프로그램, 속성, 함수, 하위 스크립트 및 관련 유형을 지정합니다. 일부 언어에서는 후속 객체의 요구 사항 사양에 대한 유사한 개념을 '인터페이스'라고합니다.
선언되고 정의 된 의정서는 명시된 요구 사항의 서명이있는 유형이며, 신속 기능이 매개 변수 및 반환의 서명을 기반으로 한 유형과 다소 유사합니다.
스위프트 프로토콜 사양은 선택 사양이거나 명시 적으로 요구되거나 프로토콜 확장이라고하는 기능을 통해 기본 구현을 제공 할 수 있습니다. 모든 지정된 요구 사항에 대해 Extension으로 구현 된 프로토콜을 준수하기를 원하는 Swift Object Type (클래스, 구조체 또는 열거 형)은 완전히 준수해야한다는 요구 사항을 명시해야합니다. 프로토콜 확장의 기본 구현 기능은 의정서 준수의 모든 의무를 이행하기에 충분할 수 있습니다.
프로토콜은 다른 프로토콜에 의해 상속 될 수 있습니다. 이것은 프로토콜 확장과 함께 프로토콜이 스위프트의 중요한 특징으로 간주 될 수 있고 또 그렇게되어야 함을 의미합니다.
프로토콜 및 확장은 프로그램 설계 유연성 및 개발 프로세스에 대한 Swift의 광범위한 목표와 접근 방식을 실현하는 데 중요합니다. Swift의 프로토콜 및 확장 기능의 주요 목적은 프로그램 아키텍처 및 개발의 구성 디자인을 용이하게하는 것입니다. 이를 프로토콜 지향 프로그래밍이라고합니다. 피곤한 옛 타이머는 OOP 디자인에 초점을 맞추고이 우수한 제품을 고려합니다.
프로토콜 은 모든 구조체 , 클래스 또는 열거 형 으로 구현할 수있는 인터페이스를 정의합니다.
protocol MyProtocol {
init(value: Int) // required initializer
func doSomething() -> Bool // instance method
var message: String { get } // instance read-only property
var value: Int { get set } // read-write instance property
subscript(index: Int) -> Int { get } // instance subscript
static func instructions() -> String // static method
static var max: Int { get } // static read-only property
static var total: Int { get set } // read-write static property
}
프로토콜에 정의 된 속성 은 { get }
또는 { get set }
으로 주석되어야합니다. { get }
은 속성을 { get }
있어야한다는 것을 의미하므로 모든 속성으로 구현할 수 있습니다. { get set }
은 속성이 gettable뿐만 아니라 설정 가능해야 함을 의미합니다.
구조체, 클래스 또는 enum은 프로토콜을 준수 할 수 있습니다.
struct MyStruct : MyProtocol {
// Implement the protocol's requirements here
}
class MyClass : MyProtocol {
// Implement the protocol's requirements here
}
enum MyEnum : MyProtocol {
case caseA, caseB, caseC
// Implement the protocol's requirements here
}
프로토콜은 확장을 통해 요구 사항에 대한 기본 구현 을 정의 할 수도 있습니다.
extension MyProtocol {
// default implementation of doSomething() -> Bool
// conforming types will use this implementation if they don't define their own
func doSomething() -> Bool {
print("do something!")
return true
}
}
프로토콜 종류로 사용할 수 있습니다, 그것은하지 않습니다 제공 associatedtype
요구 사항을 :
func doStuff(object: MyProtocol) {
// All of MyProtocol's requirements are available on the object
print(object.message)
print(object.doSomething())
}
let items : [MyProtocol] = [MyStruct(), MyClass(), MyEnum.caseA]
여러 프로토콜을 준수하는 추상 형식을 정의 할 수도 있습니다.
Swift 3 이상에서는 앰퍼샌드 ( &
)로 프로토콜 목록을 구분하여이 작업을 수행합니다.
func doStuff(object: MyProtocol & AnotherProtocol) {
// ...
}
let items : [MyProtocol & AnotherProtocol] = [MyStruct(), MyClass(), MyEnum.caseA]
이전 버전에는 구문 protocol<...>
여기서 프로토콜은 꺾쇠 괄호 <>
사이의 쉼표로 구분 된 목록입니다.
protocol AnotherProtocol {
func doSomethingElse()
}
func doStuff(object: protocol<MyProtocol, AnotherProtocol>) {
// All of MyProtocol & AnotherProtocol's requirements are available on the object
print(object.message)
object.doSomethingElse()
}
// MyStruct, MyClass & MyEnum must now conform to both MyProtocol & AnotherProtocol
let items : [protocol<MyProtocol, AnotherProtocol>] = [MyStruct(), MyClass(), MyEnum.caseA]
기존 유형을 확장 하여 프로토콜을 준수 할 수 있습니다.
extension String : MyProtocol {
// Implement any requirements which String doesn't already satisfy
}
관련 유형 요구 사항
프로토콜은 associatedtype
키워드를 사용하여 연관된 유형 요구 사항 을 정의 할 수 있습니다.
protocol Container {
associatedtype Element
var count: Int { get }
subscript(index: Int) -> Element { get set }
}
연관된 유형 요구 사항을 가진 프로토콜 은 일반 제약 조건으로 만 사용할 수 있습니다 .
// These are NOT allowed, because Container has associated type requirements:
func displayValues(container: Container) { ... }
class MyClass { let container: Container }
// > error: protocol 'Container' can only be used as a generic constraint
// > because it has Self or associated type requirements
// These are allowed:
func displayValues<T: Container>(container: T) { ... }
class MyClass<T: Container> { let container: T }
만족시킬 수있는 프로토콜을 준수 유형 associatedtype
프로토콜이 기대 지정된 유형 제공함으로써, 암시 적 요구 사항을 associatedtype
표시를 :
struct ContainerOfOne<T>: Container {
let count = 1 // satisfy the count requirement
var value: T
// satisfy the subscript associatedtype requirement implicitly,
// by defining the subscript assignment/return type as T
// therefore Swift will infer that T == Element
subscript(index: Int) -> T {
get {
precondition(index == 0)
return value
}
set {
precondition(index == 0)
value = newValue
}
}
}
let container = ContainerOfOne(value: "Hello")
이 예에 명확성을 더하기 위해 일반적인 자리 표시 자 유형의 이름은 T
이며 더 적합한 이름은 Element
이며 프로토콜의 associatedtype Element
섀도 잉합니다. 컴파일러는 일반 placeholder Element
가 associatedtype Element
을 충족시키는 데 사용됨을 계속 추측합니다 associatedtype Element
요구 사항.)
associatedtype
은 또한 typealias
사용하여 명시 적으로 만족 될 수 있습니다.
struct ContainerOfOne<T>: Container {
typealias Element = T
subscript(index: Int) -> Element { ... }
// ...
}
확장 프로그램에서도 마찬가지입니다.
// Expose an 8-bit integer as a collection of boolean values (one for each bit).
extension UInt8: Container {
// as noted above, this typealias can be inferred
typealias Element = Bool
var count: Int { return 8 }
subscript(index: Int) -> Bool {
get {
precondition(0 <= index && index < 8)
return self & 1 << UInt8(index) != 0
}
set {
precondition(0 <= index && index < 8)
if newValue {
self |= 1 << UInt8(index)
} else {
self &= ~(1 << UInt8(index))
}
}
}
}
준수 유형이 이미 요구 사항을 충족시키는 경우 구현할 필요가 없습니다.
extension Array: Container {} // Array satisfies all requirements, including Element
위임 패턴
델리게이트 는 코코아 및 코코아 터치 (CocoaTouch) 프레임 워크에서 사용되는 일반적인 디자인 패턴으로, 한 클래스는 일부 기능을 구현하는 책임을 다른 클래스에 위임합니다. 이것은 프레임 워크 클래스가 제네릭 기능을 구현하고 별도의 위임 인스턴스가 특정 유스 케이스를 구현하는 우려의 분리 원칙을 따릅니다.
대리자 패턴을 조사하는 또 다른 방법은 객체 통신의 관점입니다. Objects
종종 서로 이야기하고 그렇게하기 위해 객체는 다른 객체의 델리게이트가되기 위해 protocol
을 따를 필요가 있습니다. 이 설정이 완료되면 다른 객체는 흥미로운 일이 발생할 때 해당 대리인에게 다시 말합니다.
예를 들어, 사용자 인터페이스에서 데이터 목록을 표시하는보기는 표시 할 데이터를 결정하는 것이 아니라 데이터 표시 방법의 논리에 대한 책임을 져야합니다.
좀 더 구체적인 예를 들어 봅시다. 학부모와 자녀가 두 가지 클래스 인 경우 :
class Parent { }
class Child { }
그리고 부모에게 자녀로부터의 변화를 알리고 싶습니다.
Swift에서 대리자는 protocol
선언을 사용하여 구현되므로 delegate
이 구현할 protocol
을 선언합니다. 여기에서 대리자는 parent
개체입니다.
protocol ChildDelegate: class {
func childDidSomething()
}
자식은 대리자에 대한 참조를 저장하는 속성을 선언해야합니다.
class Child {
weak var delegate: ChildDelegate?
}
delegate
변수는 선택적이며 ChildDelegate
프로토콜은 클래스 유형에 의해서만 구현되도록 표시되어 있습니다 (이 없이는 delegate
변수는 모든 보유주기를 피하기 위해 weak
참조로 선언 될 수 없습니다.) 이것은 delegate
변수가 더 이상 존재하지 않으면 그 밖의 다른 곳에서는 참조 할 수 있습니다. 따라서 부모 클래스는 필요하고 사용할 수있는 경우에만 대리자를 등록합니다.
또한 우리 대리자를 weak
표시하려면 프로토콜 선언에 class
키워드를 추가하여 ChildDelegate 프로토콜을 참조 유형으로 제한해야합니다.
이 예에서, 아이가 무언가를하고 부모에게 통보해야 할 때, 아이는 다음을 호출합니다 :
delegate?.childDidSomething()
위임자가 정의 된 경우 위임자에게 자녀가 무언가를했음을 알립니다.
부모 클래스는 그 동작에 응답 할 수 있도록 ChildDelegate
프로토콜을 확장해야합니다. 이 작업은 상위 클래스에서 직접 수행 할 수 있습니다.
class Parent: ChildDelegate {
...
func childDidSomething() {
print("Yay!")
}
}
또는 확장 프로그램 사용 :
extension Parent: ChildDelegate {
func childDidSomething() {
print("Yay!")
}
}
부모는 또한 아동에게 그 아동의 대표자임을 알릴 필요가 있습니다.
// In the parent
let child = Child()
child.delegate = self
기본적으로 Swift protocol
은 선택적 기능을 구현할 수 없습니다. 프로토콜이 @objc
속성 및 optional
수정 자로 표시되어있는 경우에만 지정할 수 있습니다.
예를 들어, UITableView
는 iOS에서 표보기의 일반적인 동작을 구현하지만 사용자는 특정 셀이 어떻게 보이고 동작하는지 구현하는 UITableViewDelegate
및 UITableViewDataSource
라는 두 개의 대리자 클래스를 구현해야합니다.
@objc public protocol UITableViewDelegate : NSObjectProtocol, UIScrollViewDelegate { // Display customization optional public func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) optional public func tableView(tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) optional public func tableView(tableView: UITableView, willDisplayFooterView view: UIView, forSection section: Int) optional public func tableView(tableView: UITableView, didEndDisplayingCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) ... }
클래스 정의를 변경하여이 프로토콜을 구현할 수 있습니다. 예를 들면 다음과 같습니다.
class MyViewController : UIViewController, UITableViewDelegate
프로토콜 정의 (이 경우 UITableViewDelegate
에서 optional
으로 표시되지 않은 메소드는 구현되어야합니다.
특정 준수 클래스에 대한 프로토콜 확장
특정 클래스에 대한 기본 프로토콜 구현 을 작성할 수 있습니다.
protocol MyProtocol {
func doSomething()
}
extension MyProtocol where Self: UIViewController {
func doSomething() {
print("UIViewController default protocol implementation")
}
}
class MyViewController: UIViewController, MyProtocol { }
let vc = MyViewController()
vc.doSomething() // Prints "UIViewController default protocol implementation"
RawRepresentable 프로토콜 (확장 가능 열거 형) 사용
// RawRepresentable has an associatedType RawValue.
// For this struct, we will make the compiler infer the type
// by implementing the rawValue variable with a type of String
//
// Compiler infers RawValue = String without needing typealias
//
struct NotificationName: RawRepresentable {
let rawValue: String
static let dataFinished = NotificationNames(rawValue: "DataFinishedNotification")
}
이 구조체는 다른 곳으로 확장되어 케이스를 추가 할 수 있습니다.
extension NotificationName {
static let documentationLaunched = NotificationNames(rawValue: "DocumentationLaunchedNotification")
}
그리고 인터페이스는 모든 RawRepresentable 유형 또는 특히 enum 구조체를 중심으로 디자인 할 수 있습니다.
func post(notification notification: NotificationName) -> Void {
// use notification.rawValue
}
호출 사이트에서 타입 안전 NotificationName에 대한 점 구문 축약을 사용할 수 있습니다.
post(notification: .dataFinished)
일반 RawRepresentable 함수 사용
// RawRepresentable has an associate type, so the
// method that wants to accept any type conforming to
// RawRepresentable needs to be generic
func observe<T: RawRepresentable>(object: T) -> Void {
// object.rawValue
}
클래스 전용 프로토콜
프로토콜 만 지정할 수 있습니다 클래스 사용을 통해 그것을 구현할 수있는 class
의 상속 목록에 키워드를. 이 키워드는이 목록에서 상속 된 다른 프로토콜보다 앞에 나와야합니다.
protocol ClassOnlyProtocol: class, SomeOtherProtocol {
// Protocol requirements
}
비 클래스 유형이 ClassOnlyProtocol
을 구현하려고하면 컴파일러 오류가 생성됩니다.
struct MyStruct: ClassOnlyProtocol {
// error: Non-class type 'MyStruct' cannot conform to class protocol 'ClassOnlyProtocol'
}
다른 프로토콜은 ClassOnlyProtocol
에서 상속받을 수 있지만 동일한 클래스 전용 요구 사항을 갖습니다.
protocol MyProtocol: ClassOnlyProtocol {
// ClassOnlyProtocol Requirements
// MyProtocol Requirements
}
class MySecondClass: MyProtocol {
// ClassOnlyProtocol Requirements
// MyProtocol Requirements
}
클래스 전용 프로토콜의 참조 의미
클래스 전용 프로토콜을 사용하면 준수 유형을 알 수 없을 때 참조 의미를 사용할 수 있습니다.
protocol Foo : class {
var bar : String { get set }
}
func takesAFoo(foo:Foo) {
// this assignment requires reference semantics,
// as foo is a let constant in this scope.
foo.bar = "new value"
}
이 예에서 Foo
는 클래스 전용 프로토콜이므로 컴파일러에서 foo
가 클래스 유형이므로 참조 의미 체계가 있으므로 bar
대한 할당은 유효합니다.
Foo
가 클래스 전용 프로토콜이 아닌 경우 컴파일러 오류가 발생합니다. 일치하는 유형은 값 유형일 수 있기 때문에 가변적 인 var
annotation이 필요합니다.
protocol Foo {
var bar : String { get set }
}
func takesAFoo(foo:Foo) {
foo.bar = "new value" // error: Cannot assign to property: 'foo' is a 'let' constant
}
func takesAFoo(foo:Foo) {
var foo = foo // mutable copy of foo
foo.bar = "new value" // no error – satisfies both reference and value semantics
}
프로토콜 유형의 약한 변수
프로토콜 유형의 변수에 weak
변경자 를 적용 할 때 weak
은 참조 유형에만 적용될 수 있기 weak
해당 프로토콜 유형은 클래스 전용이어야합니다.
weak var weakReference : ClassOnlyProtocol?
Hashable 프로토콜 구현하기
Sets
Dictionaries(key)
에서 사용되는 형식은 Equatable
프로토콜에서 상속 한 Hashable
프로토콜을 준수해야합니다.
Hashable
프로토콜을 준수하는 사용자 정의 유형이 구현해야합니다.
- 계산 된 프로퍼티
hashValue
- 항등 연산자 ie
==
또는!=
중 하나를 정의하십시오.
다음 예제는 사용자 정의 struct
대해 Hashable
프로토콜을 구현합니다.
struct Cell {
var row: Int
var col: Int
init(_ row: Int, _ col: Int) {
self.row = row
self.col = col
}
}
extension Cell: Hashable {
// Satisfy Hashable requirement
var hashValue: Int {
get {
return row.hashValue^col.hashValue
}
}
// Satisfy Equatable requirement
static func ==(lhs: Cell, rhs: Cell) -> Bool {
return lhs.col == rhs.col && lhs.row == rhs.row
}
}
// Now we can make Cell as key of dictonary
var dict = [Cell : String]()
dict[Cell(0, 0)] = "0, 0"
dict[Cell(1, 0)] = "1, 0"
dict[Cell(0, 1)] = "0, 1"
// Also we can create Set of Cells
var set = Set<Cell>()
set.insert(Cell(0, 0))
set.insert(Cell(1, 0))
참고 : 사용자 정의 유형의 여러 값이 서로 다른 해시 값을 가질 필요는 없으며 충돌은 허용됩니다. 해시 값이 같으면 평등 연산자가 실제 평등을 결정하는 데 사용됩니다.