## Class-Only Protocol
- AnyObject : 클래스만 채택가능한 프로토콜을 만들어줌
```swift
protocol Person {
func sleep()
}
//Class-Only Protocols
protocol PersonObject: **AnyObject**, Person {
}
// ✅ Class-Only Protocols 채택
class Student1: PersonObject {
func sleep(){
print("클래스는 채택가능")
}
}
// ⚠️ Non-class type 'Student2' cannot conform to class protocol 'PersonObject'
struct Student2: PersonObject {
func sleep(){
print("struct은 채택 불가능")
}
}
프로퍼티 요구사항
프로토콜에 선언되는 프로퍼티는 초기값을 할당할 수 없다. 따라서 var 키워드를 이용하여 선언되고 get 과 set이 가능하다.
var 은 property 의 가변성과 관련이 없고 무조건 붙여야한다.
stored property 혹은 computed property 인지 명시하지 않는다.
프로토콜을 채택한 타입은 프로토콜이 요구하는 프로퍼티의 이름과 타입만 맞도록 구현하면 됨.
property 의 가변성은 변수 타입 뒤에 붙는 { get set } 의 조합으로 결정된다.
{ get set } 존재 : { get set } 모두 가구현해야함.
{ get } 만 존재 : { get } 과 { get set } 둘 중 하나만 구현해도 된다.
{ set } 만 존재할 수 없다.
protocol SomeProtocol {
var settableProperty: String { get set }
var notNeedToBeSettableProperty: String { get }
}
protocol AnotherProtocol {
static var someTypeProperty: Int { get set }
static var anotherTypeProperty: Int { get }
}
SomeProtocol 프로토콜을 정의하고, 변수로 두 개의 프로퍼티를 선언했다. settableProperty은 읽기/쓰기 모두 가능한 프로퍼티, notNeedToBeSettableProperty은 읽기만 가능하다.
AnotherProtocol은 타입 프로퍼티인데 반드시 static 키워드를 붙여 정의해야 한다.
클래스의 타입 프로퍼티에는 상속 가능한 타입 프로퍼티인 class 타입 프로퍼티와 상속 불가능한 static 타입 프로퍼티가 있는데, 이 두 타입 프로퍼티를 따로 구분하지 않고 모두 static 키워드를 사용하여 타입 프로퍼티를 요구하면 된다.
static : 오버라이딩 금지
class : 오버라이딩 허용
메서드 요구사항
프로토콜은 특정 인스턴스 메서드나 타입 메서드를 요구할 수 있다.
프로토콜이 요구할 메서드는 프로토콜 정의에서 작성한다.
메서드는 일반 인스턴스 및 타입 메서드와 완전히 동일하지만, 실제 구현부인 중괄호 { } 와 메서드 본문 없이 프로토콜 정의의 일부로 작성된다.
매개변수 기본값을 지정할 수 없다.
가변 파라미터는 일반 메서드와 동일하게 작성할 수 있다.
타입 메서드를 요구할 경우 static 키워드를 사용하고, static 키워드를 사용해 요구한 타입 메서드를 클래스에서 구현할 때는 static 키워드나 class 키워드 어느 쪽을 사용하든 상관없다.
일반적으로 선언된 메소드들은 하나라도 구현하지 않으면 에러를 발생시킨다.
선택적인 메소드 구현을 위해서는 @objc 키워드와 optional을 이용
protocol키워드 앞과 해당 함수의 앞에 @objc와 optional 키워드
@objc protocol ComputerScienceStudent{
var name:String { get }
var laptop:String? { get set }
func doDataStructure()
func doOperatingSystem()
func doNetwork()
@objc optional func doGraphics()
}
static 키워드를 사용해 요구한 타입 메서드를 클래스에서 구현시 static / class 키워드 모두 사용가능
// 프로토콜 정의
protocol SomeProtocol {
static func someTypeMethod()
static func anotherTypeMethod()
}
// 구현부
class SomeClass: SomeProtocol {
static func someTypeMethod() {
}
class func anotherTypeMethod() { // ✅자식 클래스에서 재정의 가능
}
}
// ✅자식 클래스에서 재정의 가능
class SubClass: SomeClass {
override static func anotherTypeMethod() {
}
}
Mutating : 가변메서드 요구사항
가끔은 메서드가 인스턴스 내부의 값을 변경할 때가 있다.
프로토콜이 어떤 타입이든 인스턴스 내부의 값을 변경해야 하는 메서드를 요구하려면 프로토콜의 메서드 정의 앞에 mutating 키워드를 명시해야 한다.
이로 인해 구조체와 열거형에서 프로토콜을 채택하고, 해당 메서드 요구사항을 충족시킬 수 있다.
값 타입인 클래스에서는 mutating 키워드 명시 불필요.
프로토콜에 mutating 키워드를 사용한 메서드 요구가 존재해도, 클래스 구현에서는 mutating 키워드를 사용하지 않아도 된다.
protocol Togglable {
mutating func toggle()
}
enum OnOffSwitch: Togglable {
case off, on
mutating func toggle() {
switch self {
case .off:
self = .on
case .on:
self = .off
}
}
}
var lightSwitch = OnOffSwitch.off
lightSwitch.toggle()
// lightSwitch is now equal to .on
구조체, 열거형은 값 타입 이기 때문에 메서드 안에서 프로퍼티 값을 변경하지 못하지만, mutating 키워드를 사용하여 그것을 가능하게 해 준다. 참조형인 클래스에서는 mutating 키워드가 필요하지 않다.
protocol SomeProtocol{
mutating func SomeMethod(_ num : Int)
}
// 값타입 -> mutating 으로 메소드안에서 프로퍼티 값 변경
struct SomeStruct : SomeProtocol{
var x = 0
mutating func SomeMethod(_ num :Int) {
x += num
}
}
// 참조형 -> mutating 필요 X
class SomeStruct : SomeProtocol{
var x = 0
func SomeMethod(_ num :Int) {
x += num
}
}
이니셜라이저 요구사항
프로토콜은 프로토콜을 준수하는 타입에게 특정한 이니셜라이저를 구현하도록 요구 가능하다.
이니셜라이저를 요구하려면 메서드 요구와 마찬가지로 이니셜라이저를 정의하지만 구현은 하지 않는다.
// 정의 : 매개변수만 지정
protocol Named {
var name: String { get }
init(name: String)
}
// 구현
struct Student: Named {
var name: String
init(name: String) {
self.name = name
}
}
Protocol 을 채택한 Class의 Initializer 구현
프로토콜을 채택한 타입은 Initializer를 designated initializer나 convenience initializer로 구현할 수 있습니다.
두 경우 모두 required 키워드를 initializer 앞에 붙여주어야 합니다.
이유 : SomeClass 를 서브클래싱한 클래스들도 해당 프로토콜을 채택(conform) 해야 하기 때문입니다.
단, protocol를 구현하는 클래스가 final class 라면 required 를 붙일 필요가 없습니다.
이유 : final class는 상속 (서브클래싱) 불가
+) protocol 선언부에서 initializer앞에 final 을 붙일 수 없다
class SomeClass: SomeProtocol {
required init(someParameter: Int) {
// implementation code
}
}
/ required 키워드가 없어도 Compile Error 가 발생하지 않는다./
final class myClass: SomeProtocol {
init(number: Int) {
...
}
}
하나의 매개변수가 여러 프로토콜을 모두 준수하는 타입이어야 한다면 조합하여 사용 가능
→ SomeProtocol & AnotherProtocol 처럼 조합하여 표현한다
protocol Named{
var name: String { get }
}
protocol Aged{
var age: Int { get }
}
func celebrateBirthday(to celebrator: Named & Aged){
print("Happy bday \(celebrator.name)!! You are \(celebrator.age)")
}
Delegation 을 위한 프로토콜
Delegation : 클래스나 구조체가 자신의 책임이나 임무를 다른 타입의 인스턴스에게 위임하는 디자인 패턴
ex. UITableView 타입의 인스턴스가 해야 하는 일을 위임받아 처리하는 인스턴스는 UITableViewDelegate 프로토콜을 준수하면 됨
(추가) 서브클래싱 vs 서브타이핑
상속하는 방식에 따라 이 두개의 차이가 갈린다.
서브클래싱 : 기존에 구현되어 있는 클래스를 통해 새로운 클래스를 만드는 것
우리가 아는 "상속받는 과정"
서브클래싱을 통해 부모클래스에 구현된 코드와 구조를 자식 클래스가 물려받을 수 있다.
자식클래스는 부모 클래스이 코드를 재사용할 수 있을 뿐만 아니라 자기 자신만의 고유한 내용을 추가할 수 있다.
서브타이핑 : 정의되어 있는 인터페이스를 구현하는 것
SuperType 의 객체가 수행할 행동의 약속을 (protocol) 을 SubType 이 이어 받는다.
Protocol
Protocol 이란
프로토콜은 특정 작업 혹은 기능들을 구현하기 위한 메소드, 프로퍼티 그리고 기타 다른 요구사항들의 청사진이다. 프로토콜은 스위프트에서 자바의 interface와 비슷한 역할을 한다.
프로토콜은 요구사항으로 프로토콜에서는 구현을 하지 않는다. 하지만 프로토콜을 채택(adopt) 한다면, 반드시 요구사항을 모두 구현해야한다.
프로토콜은 프로토콜 간 상속을 지원하며, 클래스와 달리 다중 상속도 지원한다. 즉, 하나의 프로토콜은 여러개의 프로토콜을 동시에 상속받을 수 있다.
프로토콜을 채택할 수 있는 타입 : enum, struct, class
Syntax
protocol Person { func sleep() }
// 프로토콜을 채택한다. protocol Student: Person { func sleep() { } }
프로퍼티 요구사항
SomeProtocol 프로토콜을 정의하고, 변수로 두 개의 프로퍼티를 선언했다. settableProperty은 읽기/쓰기 모두 가능한 프로퍼티, notNeedToBeSettableProperty은 읽기만 가능하다.
AnotherProtocol은 타입 프로퍼티인데 반드시 static 키워드를 붙여 정의해야 한다.
클래스의 타입 프로퍼티에는 상속 가능한 타입 프로퍼티인 class 타입 프로퍼티와 상속 불가능한 static 타입 프로퍼티가 있는데, 이 두 타입 프로퍼티를 따로 구분하지 않고 모두 static 키워드를 사용하여 타입 프로퍼티를 요구하면 된다.
메서드 요구사항
일반적으로 선언된 메소드들은 하나라도 구현하지 않으면 에러를 발생시킨다.
protocol키워드 앞과 해당 함수의 앞에 @objc와 optional 키워드
프로토콜에서 메서드 작성법
타입메서드 요구사항
Mutating : 가변메서드 요구사항
가끔은 메서드가 인스턴스 내부의 값을 변경할 때가 있다.
구조체, 열거형은 값 타입 이기 때문에 메서드 안에서 프로퍼티 값을 변경하지 못하지만, mutating 키워드를 사용하여 그것을 가능하게 해 준다. 참조형인 클래스에서는 mutating 키워드가 필요하지 않다.
이니셜라이저 요구사항
Protocol 을 채택한 Class의 Initializer 구현
프로토콜을 채택한 타입은 Initializer를 designated initializer나 convenience initializer로 구현할 수 있습니다.
required
키워드를 initializer 앞에 붙여주어야 합니다.단, protocol를 구현하는 클래스가
final class
라면required
를 붙일 필요가 없습니다.final class
는 상속 (서브클래싱) 불가final
을 붙일 수 없다// implementation code } }
/ required 키워드가 없어도 Compile Error 가 발생하지 않는다./ final class myClass: SomeProtocol { init(number: Int) { ... } }
Protocol as Type
프로토콜은 First Class Citizen (일급시민) 이기 때문에, 하나의 독립적인 타입 ( a fully fledged types ) 으로 사용할 수 있다.
Protocol Conformance
프로토콜을 채용하고 있는지 확인하는데에는 타입캐스팅 연산자를 사용합니다. ( is, as, as?, as! )
프로토콜 조합
하나의 매개변수가 여러 프로토콜을 모두 준수하는 타입이어야 한다면 조합하여 사용 가능 → SomeProtocol & AnotherProtocol 처럼 조합하여 표현한다
Delegation 을 위한 프로토콜
Delegation : 클래스나 구조체가 자신의 책임이나 임무를 다른 타입의 인스턴스에게 위임하는 디자인 패턴 ex. UITableView 타입의 인스턴스가 해야 하는 일을 위임받아 처리하는 인스턴스는 UITableViewDelegate 프로토콜을 준수하면 됨
(추가) 서브클래싱 vs 서브타이핑
상속하는 방식에 따라 이 두개의 차이가 갈린다.
서브클래싱 : 기존에 구현되어 있는 클래스를 통해 새로운 클래스를 만드는 것
서브타이핑 : 정의되어 있는 인터페이스를 구현하는 것
https://epicdevsold.tistory.com/177 https://taekki-dev.tistory.com/38
Reference