Swift 기본 문법 ⓖ (Protocol, Generics)

krystal·2022년 10월 11일
0

Swift 기초

목록 보기
7/11
post-thumbnail

The Swift Language Guide (번역본)
Swift) 제네릭(Generic) 정복하기
프로토콜 | 야곰 swift 블로그

Protocol

  1. 프로토콜(Protocol) 은 특정 역할을 수행하기 위한 메서드, 프로퍼티, 기타 요구사항 등의 청사진을 정의한다
  2. 구조체, 클래스, 열거형은 프로토콜을 채택(Adopted) 해서 특정 기능을 수행하기 위한 프로토콜의 요구사항을 실제로 구현할 수 있다.
  3. 어떤 프로토콜의 요구사항을 "모두" 따르는 타입은 프로토콜을 준수한다(Conform) 고 표현
  4. protocol 키워드를 사용한다.
  5. 프로퍼티 요구는 항상 var 키워드를 사용한다.
protocol 프로토콜 이름 {
   
}

프로토콜의 구현

protocol Talkable {
    
    // 프로퍼티 요구

    // get은 읽기만 가능해도 상관 없다는 뜻이며
    // get과 set을 모두 명시하면 
    // 읽기 쓰기 모두 가능한 프로퍼티여야 한다

	var topic: String { get set }
    var language: String { get }
    
    // 메서드 요구
    func talk()
    
    // 이니셜라이저 요구
    init(topic: String, language: String)
}

프로토콜 채택 및 준수

// Person 구조체는 Talkable 프로토콜을 "채택"

struct Person: Talkable {
    
    // 프로퍼티 요구 "준수"
    var topic: String
    let language: String
    
    // 메서드 요구 "준수"    
    func talk() {
        print("\(topic)에 대해 \(language)로 말합니다")
    }

    // 이니셜라이저 요구 "준수"    
    init(topic: String, language: String) {
        self.topic = topic
        self.language = language
    }

프로토콜 상속

프로토콜은 하나 이상의 프로토콜을 상속받아 기존 프로토콜의 요구사항보다 더 많은 요구사항을 추가할 수 있다.
프로토콜 상속 문법은 클래스의 상속 문법과 유사하나 프로토콜은 클래스와 다르게 다중상속이 가능.

protocol 프로토콜 이름: 부모 프로토콜 이름 목록 {

 }

<프로토콜 상속의 예시>

protocol Readable {
    func read()
}
protocol Writeable {
    func write()
}
protocol ReadSpeakable: Readable {
    func speak()
}
protocol ReadWriteSpeakable: Readable, Writeable {
    func speak()
}

struct SomeType: ReadWriteSpeakable {
    func read() {
        print("Read")
    }
    
    func write() {
        print("Write")
    }
    
    func speak() {
        print("Speak")
    }
}

클래스 상속과 프로토콜

클래스에서 상속과 프로토콜 채택을 동시에 하려면
1. 상속받으려는 클래스를 먼저 명시하고
2. 그 뒤에 채택할 프로토콜 목록을 작성

class SuperClass: Readable {
    func read() { }
}

class SubClass: SuperClass, Writeable, ReadSpeakable {
    func write() { }
    func speak() { }
}

프로토콜 준수확인

is, as 연산자를 사용해서 인스턴스가 특정 프로토콜을 준수하는지 확인할 수 있다.


// SuperClass : Readable
// SubClass : SuperClass, writeable, ReadSpeakable

let sup: SuperClass = SuperClass()
let sub: SubClass = SubClass()

var someAny: Any = sup
someAny is Readable // true
someAny is ReadSpeakable // false

someAny = sub
someAny is Readable // true
someAny is ReadSpeakable // true

someAny = sup

if let someReadable: Readable = someAny as? Readable {
    someReadable.read()
} // read

if let someReadSpeakable: ReadSpeakable = someAny as? ReadSpeakable {
    someReadSpeakable.speak()
} // 동작하지 않음

someAny = sub

if let someReadable: Readable = someAny as? Readable {
    someReadable.read()
} // read



Generics

타입에 의존하지 않는 범용 코드를 작성할 때 사용
제네릭은 더 유연하고 재사용 가능한 함수와 타입의 코드를 작성하는 것을 가능케한다.


func swapTwoInts(_ a: inout Int, _ b: inout Int) {
    let temporaryA = a
    a = b
    b = temporaryA
}

(C언어에서의 템플릿과 비슷한 흐름인 듯 하다)
만약 내가 함수를 작성을 했는데, 내용은 같지만 상황에 따라 매개변수 타입이 각각 다르게 넣고 싶다면?

func swapTwoStrings(_ a: inout String, _ b: inout String) {
    let temporaryA = a
    a = b
    b = temporaryA
}

func swapTwoDoubles(_ a: inout Double, _ b: inout Double) {
    let temporaryA = a
    a = b
    b = temporaryA
}

Swift는 타입에 예민한 언어이기 때문에 매개변수 타입에 따라 함수를 계속해서 오버로딩해야할 것이다
제너릭 을 통해서 타입에 제한을 두지않게 만든다.


제너릭을 사용한 함수

실제 함수를 호출할 때마다 해당 매개변수 타입으로 바뀌게 된다.

func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
    let temporaryA = a
    a = b
    b = temporaryA
}
// 사용할 이름 뒤에 <T>를 붙임.


var someString = "hello"
var anotherString = "world"
swapTwoValues(&someString, &anotherString)
// someString is now "world", and anotherString is now "hello"

제너릭은 함수 뿐만 아니라 구조체, 클래스, 열거형에서도 사용이 가능하다.

struct Stack<Element> {
    var items = [Element]()
    mutating func push(_ item: Element) {
        items.append(item)
    }
    mutating func pop() -> Element {
        return items.removeLast()
    }
}

타입 제약

제네릭 함수를 선언할 때
파라미터 뒤에 상속 받아야 하는 클래스를 선언하거나 반드시 따라야 하는 프로토콜을 명시할 수 있다.

func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
    // function body goes here
}

클래스 제약

class Apple { }
class Fruit { }
class Banana: Human { }
 
func sayIt<T: Human>(_ a: T) { }



let apple = Apple.init()
let fruit = Fruit.init()
let banana = Banana.init()
 
SayIt(bird)     // error
profile
https://source-coding.tistory.com/

0개의 댓글