2023년 1월 1일
08:00 AM
Buffering ...

최근 글 👑

[Swift-Study] 심화 문법종합반 2주차 4일차 정리 - 프로토콜

2024. 3. 27. 16:55ㆍIOS/Swift-Study
SMALL

프로토콜

메서드, 속성 및 기타 요구사항의 청사진을 정의하여 클래스,

구조체 또는 열거형에서 구현할 수 있는 일종의 인터페이스인 '프로토콜'에 대해 알아봅시다.

 

- 특정 역할을 하기 위한 메소드, 프로퍼티, 기타 요구사항 등을 정의 해놓은 “규약” 혹은 “약속”

- "class", "structure", "enum"이 프로토콜을 ‘채택’하고 모든 요구사항을 충족하면 프로토콜을 ‘준수’함

- class, structure, enum이 프로토콜을 채택해서 특정 기능을 실행하기 위한 요구사항을 실제로 구현

- 설계된 조건만 정의를 하고 제시를 할 뿐 스스로의 기능 구현은 안합니다.

- 프로토콜에서는 이름과 타입 그리고 gettable, settable을 명시

- 프로퍼티는 항상 var로 선언

- 메서드를 정의할 때 메서드 이름과 리턴값을 지정할 수 있고, {}(구현 코드)는 적지 않습니다.

- 상속과 유사하다고 볼 수도 있겠지만 class 이외에 struct나 enum도 프로토콜을 채택하는 특징을 지님

- 상속은 다중 상속이 불가능, 프로토콜은 다중 상속 가능 (확장성이 높음)

// 프로토콜 예시 1
protocol 프로토콜이름 {
 // 프로토콜 정의
}

// 상속받는 클래스의 프로토콜 채택
class 클래스이름: 슈퍼클래스, 프로토콜1, 프로토콜2 { // 상속의 경우 콤마(,)를 통해서 상속과 프로토콜 채택을 동시에 사용 
 // 클래스 정의
}

protocol Vehicle {
    var speed: Double { get set } // get과 set을 모두 요구하는 가변 속성
    var manufacturer: String { get } // 읽기 전용 속성
    /*
     speed가 현재 얼마인지를 'get' 얻어 올 수 있으며, speed가 몇인지 'set' 넣어줄 수 있다는 의미입니다.
     manufacturer는 'get' 값을 얻어 올 수 있음을 이야기 합니다.
    */
}

class Car: Vehicle { // Vehicle이라는 프로토콜을 Car가 채택을 한 코드
    var speed: Double = 0.0 // get과 set이 요구되는 속성을 구현
    var manufacturer: String = "Toyota" // 읽기 전용 속성을 구현
}

class Bicycle: Vehicle { // class이기 때문에 실제로 값을 넣어주어야 하며, 넣고 싶지 않다면 init을 만들어 주어야 합니다.
    var speed: Double = 0.0 // get과 set이 요구되는 속성을 구현
    var manufacturer: String { return "Giant" } // 읽기 전용 속성을 연산 프로퍼티로 구현
    /*
     speed는 get과 set이 요구
     manufacturer get 읽기가 가능한 형태이다.
    */
}
/*
 Car() 인스턴스를 만들고 여기에 speed는 60.0 이는 set이며 
 값을 넣어주는 것이며, car의 speed를 get 할수도 있습니다.
 그리고 car의 manufacturer를 get할수도 있습니다.
*/
let car = Car()
car.speed = 60.0 // set 가능
print(car.speed) // get 가능
print(car.manufacturer) // get 가능

let bike = Bicycle()
bike.speed = 20.0 // set 가능
print(bike.speed) // get 가능
print(bike.manufacturer) // get 가능
// 프로토콜 예시 2
protocol Student {
    var studentId: Int { get set }
    var name: String { get }
    func printInfo() -> String
}

struct UnderGraduateStudent: Student {
    var studentId: Int
    var name: String
    var major: String
    
    func printInfo() -> String {
        return "\(name), whose student id is \(studentId), is major in \(major)"
    }
}

struct GraduateStudent: Student {
    var studentId: Int
    var name: String
    var degree: String
    var labNumber: Int
    
    func printInfo() -> String {
        return "\(name), member of lab no.\(labNumber), has a \(degree) degree"
    }
}

// 프로토콜은 타입으로서도 사용가능
let underGraduate: Student = UnderGraduateStudent(studentId: 1, name: "홍길동", major: "computer")
let graduate: Student = GraduateStudent(studentId: 2, name: "김철수", degree: "master", labNumber: 104)

let studentArray: [Student] = [underGraduate, graduate]
// 프로토콜의 다중상속
protocol Coordination
 {
    var top: String { get set }
    var pants: String { get set }

    init(top: String, pants: String)

    func checkCoordination()
}

protocol Hair {
    var hair: String { get }

    func checkHairStyle()
}

struct Person: Coordination, Hair { // 코디와 헤어스타일을 두개다 챙기기위해서..
    var top: String
    var pants: String
    let hair: String = "포마드"

    func checkHairStyle() {
        print("오늘의 헤어스타일은 \(hair)스타일")
    }

    func checkCoordination() {
        print("상의: \(top)\n하의: \(pants)")
    }

    init(top: String, pants: String) { // 위 init에서 선정한 대로
        self.top = top
        self.pants = pants
    }     
}

let safari: Person = Person(top: "긴팔", pants: "반바지")
safari.checkHairStyle()
safari.checkCoordination()
//오늘의 헤어스타일은 포마드스타일
//상의: 긴팔
//하의: 반바지

associatedtype, typealias

associatedtype

- 프로토콜 내에서 실제 타입을 명시 X

- 해당 프로토콜을 채택하는 타입에서 실제 타입을 결정에 사용

- 프로토콜에서 특정 메서드, 속성 또는 서브스크립트의 반환 타입이나

- 매개변수 타입으로 구체적인 타입을 명시하지 않고

대신 "associatedtype"으로 선언하여

프로토콜을 채택하는 타입에서 실제 타입을 정의

 

typealias

- 기존 타입에 대해 새로운 이름을 지정하거나 복잡한 타입에 대한 간결한 별칭을 생성할 때 사용

- 코드를 읽기 쉽게 만들거나 여러 번 사용되는 긴 타입 이름을 간략하게 대체할 때 유용

// associatedtype, typealias 예시
/*
 Container 라는 프로토콜이 있습니다. 
 Item이라는 타입은 만든것도 아니며, 기본형도 아닙니다.
 Item은 제네릭과 엇비슷한 느낌입니다. (제네릭의 경우 T를 사용해서 모든 타입이 들어올 수 있었습니다.) 
 "typealias Item = Int" Item 타입에 Int를 넣었는데, 이는 Item이라는 타입을 쓸 것이며 별칭을 뜻합니다.
 즉, Item의 타입은 Int 타입으로 정의가 되었고, Item이 쓰인 전부가 Int 타입으로 치환되었습니다.
 
*/
protocol Container {
    associatedtype Item // 연관 타입
    var count: Int { get }
    mutating func append(_ item: Item)
    func item(at index: Int) -> Item
}

struct IntContainer: Container {
    typealias Item = Int // 연관 타입을 Int로 typealias하여 구현
    var items = [Item]() // Int의 배열
    
    var count: Int {
        return items.count
    }
    
    mutating func append(_ item: Item) {
        items.append(item)
    }
    
    func item(at index: Int) -> Item { // item 이건 타입이 아니라 메서드 이름 입니다.
        return items[index]
    }
}

var intBox = IntContainer()
intBox.append(5)
intBox.append(10)
print(intBox.item(at: 0)) // 출력: 5

/*
위 예시에서 Container 프로토콜은 Item이라는 연관 타입을 가지고 있습니다. 
이 연관 타입은 Container 프로토콜을 채택하는 구체적인 타입에서 실제 타입으로 정의 
IntContainer 구조체에서 Item을 Int로 typealias하여 실제 타입을 정의, 
이를 사용하여 배열에 Int 값을 저장하고 반환하는 메서드를 구현
*/

 

728x90