Protocol
protocol MyProtocol {
//Define requirements
}
struct Mystruct: MyProtocol {}
class MyClass: MyProtocol {}
Swift
복사
스위프트에서 프로토콜은 '자격증' 과 비슷하다.
만약 내가 구조대원 자격증을 따고 싶다면 수영, 인명구조, 심폐소생술 등을 할수 있어야 할것이다. 이러한 기술들을 보유하지 못하다면 자격증을 딸 수 없다.
프로토콜도 마찬가지이다. 특정 메소드를 의무적으로 포함하지 않으면 프로토콜을 적용했다고 볼 수 없는 것이다.
protocol 정의해보기
비행 능력을 가진 프로토콜 정의
protocol CanFly {
func fly()
}
Swift
복사
우리에게 Bird라는 Class, Airplane이라는 Structure가 있다고 가정해보자. 그리고 우리는 이들에게 비행(fly) 이라는 속성을 공통적으로 부여할 것이다.
class Bird {
var isFemale = true
func layEgg() {
if isFemale {
print("The bird makes a new bird in a shell.")
}
}
}
struct Airplane {
}
Swift
복사
만약 프로토콜을 사용하지 않았을 경우 어떤 문제가 발생할까?
상위 클래스에서 하위 클래스로의 상속으로 인한 일괄적 속성 부여 문제
만약 프로토콜을 사용하지 않고 Bird 클래스 내에 fly()을 선언하면, Bird 클래스를 상속 받은 하위 클래스인 Eagle 과 Penguin 모두 fly()가 상속된다.
class Bird {
var isFemale = true
func fly() {
print("The bird flaps its wings and lifts off into the sky.")
}
func layEgg() {
if isFemale {
print("The bird makes a new bird in a shell.")
}
}
}
class Eagle: Bird {
func soar() {
print("The eagle glides in the air using air currents.")
}
}
class Penguin: Bird {
func swim() {
print("The penguin paddles through the water.")
}
}
Swift
복사
하지만 우리는 알다시피 펭귄은 날지 못한다. 그럼에도 불구하고 다음과 같은 결과가 나올 것이다.
let myPenguin = Penguin()
myPenguin.fly()
>>> 실행결과
"The bird flaps its wings and lifts off into the sky."
Swift
복사
struct 와 호환성 문제
Structure에는 Class의 속성을 전달할 수 없다. 그래서 프로토콜없이 Airplane에 Bird의 비행 능력을 전달하려면 Airplane을 Class로 변경하여 Bird의 하위 클래스로 만드는 방법 뿐이다. 그러나 이 방법도 문제점이 많다.
class Airplane: Bird {
override func fly() {
print("The airplane uses its engine to lift off into the air.")
}
}
let airplane = Airplane()
airplane.layEgg
>>> 실행결과
"The bird makes a new bird in a shell."
Swift
복사
우선 Airplane은 Bird가 아닌데, Bird의 하위클래스가 되는 것은 이상할 뿐더러, overrice func 을 이용해 fly() 의 출력문구를 비행기에 특성에 맞추어 바꾼다하더라도 Bird의 leyEgg ()가 전달되어 알을 낳는 비행기가 탄생해버린다.
이러한 상황에서 프로토콜을 사용하면 위의 문제들이 해결된다.
아래는 fly() 기능이 있는 CanFly 프로토콜울 사용한 코드이다.
protocol CanFly {
func fly()
}
class Bird {
var isFemale = true
func layEgg() {
if isFemale {
print("The bird makes a new bird in a shell.")
}
}
}
//subclass of Bird
class Eagle: Bird, CanFly {
func soar() {
print("The eagle glides in the air using air currents.")
}
func fly() {
print("The eagle flaps its wings and lifts off into the sky.")
}
}
//subclass of Bird
class Penguin: Bird {
func swim() {
print("The penguin paddles through the water.")
}
}
struct Airplane: CanFly {
func fly() {
print("The airplane uses its engine to lift off into the air.")
}
}
Swift
복사
class 상속 도식화
protocol 사용 도식화
protocol은 다음과 같이 여러개를 사용할 수 있다.
struct MyStructure: FirstProtocol, AnotherProtocol {
// structure definition
}
class MyClass: SuperClass, FirstProtocol, AnotherProtocol {
// class definition
}
Swift
복사
Generic
제네릭이란 타입에 의존하지 않는 범용 코드를 작성할 때 사용한다. 제네릭을 사용하면 중복을 피하고, 코드를 유연하게 작성할 수 있다.
인자로 오는 두 Int 타입의 값을 swap하는 함수
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let tempA = a
a = b
b = tempA
}
Swift
복사
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let tempA = a
a = b
b = tempA
}
그러나 int 형 말고 double이나 string 로 파라미터가 들어올 경우 사용할 수 없다. 따라서 이를 해결해주기 위해 다음과 같이 제네릭을 사용할 수 있다.
func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
let tempA = a
a = b
b = tempA
}
Swift
복사
여기서 이 T를 Type Parameter라고 부르는데,
T라는 새로운 형식이 생성되는 것이 아니라,
실제 함수가 호출될 때 해당 매개변수의 타입으로 대체되는 Placeholder
왜 함수 이름 뒤에 꺽쇠(<>)로 T를 감싸냐면, 위에서 말했듯 T는 새로운 형식이 아니라 Placeholder이기 때문에, Swift한테 “T는 새로운 타입이 아니야! 그러니까 실제 이 타입이 존재하는지 찾지 마! 자리 타입이야!!” 라고 말해주기 위한 것.
Genericdms 구조체, 클래스, 열거형 타입에도 선언할 수 있는데, 이것을 "제네릭 타입(Generic Type)" 이라고 한다.
만약 Stack을 제네릭으로 만들고 싶다면 아래와 같이 선언할 수 있다.
struct Stack<T> {
let items: [T] = []
mutating func push(_ item: T) { ... }
mutating func pop() -> T { ... }
}
struct Stack<T> {
let items: [T] = []
mutating func push(_ item: T) { ... }
mutating func pop() -> T { ... }
}
Swift
복사
인스턴스를 생성할 때는 배열 생성하듯이 동일하게 해주면 된다.
let stack1: Stack<Int> = .init()
let stack2 = Stack<Int>.init()
Swift
복사
let stack1: Stack<Int> = .init()
let stack2 = Stack<Int>.init()