[Swift] - Protocol(프로토콜)

longlivedrgn·2023년 1월 23일
0

swift문법

목록 보기
36/36
post-thumbnail

💥 프로토콜(Protocol)

  • 기능에 적합한 메서드, 프로퍼티 등에 대한 청사진이다.
  • 클래스, 구조체, 열거형이 채택을 한다.
  • 약속
  • 채택, 준수, 실체화

🌟 프로토콜 정의

  • 구체적인 구현은 채택한 type이 해준다.
protocol SoccerTeam {
    var striker: String   { get set }
    var midfielder: String  { get set }
    var defender: String  { get set }
    var goalKeeper: String { get set } 

    func play(sports: String) -> String {
		}
}

🫧 프로토콜 프로퍼티 선언

  • 연산 프러퍼티 & 저장 프러퍼티 둘 다 가능하다.
    • 연산 프러퍼티 ⇒ 무조건 var로 선언
    • 저장 프러피티 ⇒ get, set에 따라서 var or let 가능하다.

1️⃣  { get } 일 경우

  • 아래와 같이 get만 있다고 가정해보자.
protocol SoccerTeam {
    var striker: String { get }
}
  • 저장 프러피티 ⇒ let, var 둘 다 가능하다.
class ATeam: SoccerTeam {
		let striker: String = "Kim"
}

class BTeam: SoccerTeam {
		var striker: String = "Kim"
}
  • 연산 프러퍼티get only / getter setter 둘 다 만들어서 사용해도 된다.
class ATeam: SoccerTeam {
		var random: String = ""
		var striker: String {
				get {
							return "kim"
				}
		}
}

class BTeam: SoccerTeam {
		var random: String = ""
		var striker: String {
				get {
							return "kim"
				}
				set {
						self.random = newValue
				}
		}
}

2️⃣  { get set } 일 경우

  • 아래와 같이 get, set 둘 다 구현줬다고 해보자.
protocol SoccerTeam {
    var striker: String { get set }
}
  • 저장 프러퍼티 ⇒ var로만 선언가능하다.
class ATeam: SoccerTeam {
		var striker: String = "Kim"
}
  • 연산 프러퍼티 ⇒ var로만 선언이 가능하고, getter, setter 모두 제공해야된다!
class BTeam: SoccerTeam {
		var random: String = ""
		var striker: String {
				get {
							return "kim"
				}
				set {
						self.random = newValue
				}
		}
}

🌟 프로토콜 채택

  • 채택 후, 실제 구현(실체화)을 아래와 같이 해준다!
class ManUtd: SoccerTeam {
    var striker: String = "A" 
    var midfielder: String = "B"
    var defender: String = "C"  
    var goalKeeper: String = "D" 

		func play(sports: String) -> String {
				return "Soccer"
		}
}

⁉️ Optional 프로퍼티

  • 아래와 같이 꼭 필요하지 않은 프로퍼티는 @objc optional을 붙혀주어서 채택한 곳에서 꼭 선언해주지 않아도 된다.
protocol SoccerTeam {
    var striker: String   { get set }
    var midfielder: String  { get set }
    var defender: String  { get set }
    @objc optional goalKeeper: String { get set } 

    func play() {
		print("축구하자"
		}
}

그러나, 이럴 경우 채택하는 타입은 struct이면 안된다! 무조건 class로! objc이 붙으면 objective-c에서 사용이 가능하다라는 뜻인데, objective-c에서는 무조건 class만 프로토콜 채택이 가능하다. 또한 자연스럽게 protocol은 AnyObject을 채택하게 된다.

⁉️ mutating 메서드

  • 구조체의 경우, mutating이 필요할 경우 protocol의 메서드에서 mutating을 붙혀준다.

🌟 1급 객체, 프로토콜

  • 1급 객체란 어떠한 객체가 하나의 독립적인 타입으로서 사용될 수 있는 것을 뜻한다.
  • 즉, 프로토콜은 하나의 독립적인 타입으로 생각할 수 있다.

🫧 프로토콜 타입을 상수, 변수 타입으로 선언하기

  • 아래와 같은 프로토콜이 존재한다고 생각해보자
protocol SoccerTeam {
    var striker: String { get }
}
  • 그리고 아래와 같이 변수의 타입으로 프로토콜을 선언할 수 있다. 아니 어떻게 상수를 프로토콜 타입으로 만들 수 있냐고? 1급 객체이기 때문이다.
let manUtd: SoccerTeam
  • 즉, 위의 내용을 자세하게 풀어보면 아래와 같다. SoccerTeam 프로토콜을 만족하는 구조체 ATeam이 있다고 가정해보자.
struct ATeam: SoccerTeam {
		var striker: String = "KIM"
		var midfielder: String = "LEE"
}
  • 그러면, ATeam이라는 구조체 인스턴스를 프로토콜 타입으로 타입캐스팅이 가능하다. 따라서 아래와 같이 코드를 작성할 수 있다.
  • 따라서 아래의 manUtd는 SoccerTeam 타입이므로, midfielder 프로퍼티에는 접근할 수 없고, striker 프로퍼티에만 접근할 수 있다.
let manUtd: SoccerTeam = ATeam.init()

🫧 프로토콜 타입을 함수의 Parameter와 Return 타입으로 활용하기

  • 아래와 같이 프로토콜 타입을 함수의 parameter 타입과 return 타입으로 설정할 수 있다.
  • 먼저 return의 경우 ATeam struct의 인스턴스를 SoccerTeam 타입으로 타입캐스팅해서 return한다.
  • 파라미터의 경우에도, ATeam struct의 인스턴스를 SoccerTeam 타입으로 타입캐스팅해서 보내준다. 따라서 파라미터는 ATeam 인스턴스가 들어가야된다.
fun doSomething(soccerTeam: SoccerTeam) -> SoccerTeam {
		return ATeam.init()
}

🌟 Protocol extension

  • 하나의 예를 가정해보자. SoccerTeam을 채택하는 여러 개의 struct이 존재하는데, 모든 structplay 함수를 구현할 때에 동일하게 구현한다면?
  • 그러면 쓸데없이 모든 struct에서 동일한 작업을 반복해주어야된다.
protocol SoccerTeam {
		fun play()
}

// 같은 함수 play()인데, 각 struct마다 반복적으로 구현해주어야된다. => 비효율적!
struct ATeam: SoccerTeam {
		func play() {
				print("play")
		}
}

struct BTeam: SoccerTeam {
		func play() {
				print("play")
		}
}  
  • 이럴 경우 extension을 통하여 프로토콜 메서드의 기본 구현을 제공할 수 있다.
  • 이렇게 할 경우, 해당 프로토콜을 채택한 곳에서 직접 play()를 구현해 줄 필요가 없다!
protocol SoccerTeam {
		fun play()
}

extension SoccerTeam {
		fun play() {
			print("play")
		}
}
  • ‼️ 그리고 메소드 말고 연산 프로퍼티도 위와 같이 extension으로 미리 구현이 가능하다!

🌟 Associatedtype

  • 프로토콜에서 Generic 기능을 사용하고 싶다면? associatedtype을 활용해야된다.
  • 아래와 같이 value라는 이름으로 범용타입을 설정할 수 있다.
protocol SoccerTeam {
    associatedtype value
    
    func attack(value: value)
    func defence() -> value
}
  • 만약 타입에 제약을 주고싶다면?
protocol SoccerTeam {
    associatedtype value: Equatable
    
    func attack(value: value)
    func defence() -> value
}

🫧 Associatedtype을 사용한 프로토콜 채택

  • typealias를 활용하여 어떤 타입으로 활용할 것인지를 명시해준다!
struct ManUtd: SoccerTeam {
	typealias value = Int
	
	  func attack(value: value) { }
    func defence() -> value { ... }
}
  • 만약 typealias 없이도 타입 추론이 가능하다면? typealias를 생략해줘도 된다!
struct ManUtd: SoccerTeam {	
	  func attack(value: Int) { }
    func defence() -> Int { ... }
}

🫧 기존 방식으로는 선언할 수 없을까?

  • 당연히 가능하다. 아래와 같이 선언이 가능하다.
protocol SoccerTeam {
    func attack<T: Equatable>(value: T)
}

0개의 댓글