大器晩成

[Protocols] 프로토콜 요구사항 본문

iOS/Swift 문법

[Protocols] 프로토콜 요구사항

zerobugpark 2025. 1. 12. 23:29

 

프로토콜 속성의 요구사항

  • 프로토콜에 선언되는 프로퍼티에는 초기값을 할당할 수 없다.
  • var로만 가능합니다. (let으로 선언 불가능)
    -> 개인적인 생각 ) 프로토콜은 선언만 할 뿐 값을 지정할 수 없기 때문에, let키워드를 사용하지 못하는 거 같다. let 초기값을 지정해야 하지만, var는 지정하지 않아도 되기 때문에, 프로토콜의 성격을 봤을 때 var 키워드만 사용하는 것이 많은 거 같다.
    또, 프로토콜에서는 함수를 정의할 때, private 키워드를 사용할 수 없고, 채택한 클래스나, 구조체에서 함수를 사용할 때에도 private 키워드를 사용할 수 없다. 이 또한 프로토콜의 성격과 맞지 않기 때문에 사용이 불가능한 거 같다. 프로토콜은 어느 곳에든지 채택만 하면 사용을 할 수 있어야 하는데, private은 해당 클래스나, 구조체 내에서만 사용이 가능하도록 하는 키워드이기 때문에, 둘의 성격은 맞지 않기 때문에, private 키워드를 프로토콜에서는 쓸 수 없는 거 같다.
  • get, set 키워드를 통해 읽기/쓰기 여부를 설정 (최소한의 요구사항으로, 연산/저장속성을 구분하지 않는다.)
  • 프로퍼티의 종류, 이름, 변수 구분, 타입, 읽기 전용인지 등에 대한 정의만 할 뿐이다.

 

  
protocol SomePropertyProtocol {

	var name: String { get } // ===> let 저장속성 / var 저장속성 / 읽기계산속성 / 읽기,쓰기 계산속성
   	var description: String { get set } // ==> var 저장곡성 / 읽기, 쓰기 계산속성
   	static var type: String {get set } // ==> 타입 저장속성 (static) 
                                       // ==> 타입 계산속성 (class)
}

struct person: SomePropertyProtocol {

    var name = "Zerobug"
    
    var description: String {
        get {
            return "Hello \(name)"
        } set {
            name = newValue
        }
    }
    
    static var type: String = "사람"

}
var zerobug = person()
zerobug.description
zerobug.name
zerobug.description = "00"
zerobug.description
person.type

 

타입속성 

1. 저장 타입 속성으로 구현

protocol SomePropertyProtocol {

    var name: String { get }
    var description: String { get set }
    static var type: String { get set }
}



class Undergraduate: SomePropertyProtocol {
    var name: String {
        return "Zerobug"
    }
    
    var description: String {
        get { "대학생입니다" }
        set { }
    }
    
    static var type: String = "사람"     // 타입 저장 속성은 (상속은 되지만) 재정의 원칙적 불가능
}
  • 저장속성은 원칙적으로 재정의가 불가능하기 때문에, class 키워드를 사용할 수 없습니다.

2. 계산 타입 속성으로 구현

class GradStudent: SomePropertyProtocol {
    var name: String {
        return "Zerobug"
    }
    
    var description: String {
        get { "대학원생입니다" }
        set { }
    }
    
    
    class var type: String {   // 타입 계산 속성은 재정의 가능 (class키워드 가능)
        get { "노예" }
        set { }
    }
}

 

  • 계산 속성은 메서드이기 때문에, 하위 타입에서 재정의 가능합니다.
  • class키워드를 사용할 경우 하위 클래스에서 재정의가 가능합니다.
  • 재정의를 원치 않을 경우 class키워드가 아닌 static 키워드를 사용하면 됩니다.

 

프로토콜 메서드의 요구사항

 

  • 메서드의 헤드 부분(인풋/아웃풋)의 형태만 요구사항으로 정의합니다.
    -> 메서드의 이름, 파라미터 타입, 파라미터 이름, 반환 타입까지 정의합니다.
  • mutating 키워드: 구조체에서 저장 속성을 변경하는 경우, 구조체도 채택 가능하도록 허락하는 키워드입니다.
  • 타입 메서드로 제한하려면, static 키워드만 붙이면 됩니다.
    (채택해서 구현하는 쪽에서 static / class 모두 사용이 가능합니다.)
protocol SomeMethodProtocol {
    func execute(cmd: String)
    func showPort(p: Int) -> String
}

struct RubyService: SomeMethodProtocol {
    func execute(cmd: String) {
        if cmd == "start" {
            print("실행합니다.")
        }
    }
    
    func showPort(p: Int) -> String {
        return "Port: \(p)"
    }
}

 

 

프로토콜 메서드 정의 방법

protocol NewMethodProtocol {
    mutating func execute (cmd command: String, desc: String)
    func showPort(p: Int, memo desc: String) -> String
}

struct RubyNewService: NewMethodProtocol {
    func execute(cmd command: String, desc: String) {
        if command == "start" {
            print("\(desc)를 실행합니다.")
        }
    }
    func showPort(p: Int, memo desc: String) -> String {
        return "Port: \(p), Memo: \(desc)"
    }
}

struct RubyNewService2: NewMethodProtocol {
    func execute(cmd comm: String, desc d: String) { //내부매개변수명 변경
        if comm == "start" {
            print("\(d)를 실행합니다.")
        }
    }
    func showPort(p: Int, memo description: String) -> String { //내부매개변수명 변경
        return "Port: \(p), Memo: \(description)"
    }
}
  • 외부 매개변수명은 프로토콜을 그대로 따라야 하지만, 내부 매개변수명은 임의로 바꾸어 사용이 가능합니다.
  • 만약 내부 매개변수명과 외부 매개변수명이 프로토콜에서 통합되어 선언되어 있을 경우 채택하는 곳에서, 이를 분리하여 내부 매개변수명과  외부 매개변수명으로 따로 작성해도 됩니다.

 

mutating 키워드

protocol SomePropertyProtocol {
   func doSomething()
}

class mutatingTest: SomePropertyProtocol {
    
    var name = "Park"
    
    func doSomething() {
        self.name = "Zerobug"
    }  
    
}

protocol SomePropertyProtocol2 {
   mutating func doSomething()
}
struct mutatingTest2: SomePropertyProtocol2 {
   
   var name = "Park"
       
    mutating func doSomething() {
        self.name = "Zerobug"
    }
    
}
  • 클래스 내 메서드의 저장속성을 변경은 가능합니다. 하지만 구조체에서는 변경이 불가능하기 때문에, mutating키워드를 사용하여 구조체 내 메서드에서 저장속성의 값을 변경이 가능하도록 해줍니다. 이때, 프로토콜에서도 mutating 키워드를 사용해서 함수의 헤더를 작성해야 합니다.

열거형에서 mutating

protocol Togglable {
    mutating func toggle()        // mutating의 키워드는 메서드 내에서 속성 변경의 의미일뿐(클래스에서 사용 가능)
}

enum OnOffSwitch: Togglable {
    case on
    case off
    
    mutating func toggle() {
        switch self {   // self는 .on  .off
        case .off:
            self = .on
        case .on:
            self = .off
        }
    }
}
var s = OnOffSwitch.off
s.toggle()
s.toggle()
  • 열거형도 구조체와 마찬가지로 값 타입이기 때문에 mutating 키워드를 사용해야 합니다.

클래스의 경우

class BigSwitch: Togglable {
    var isOn = false
    
    func toggle() {      // mutating 키워드 필요없음 (클래스 이기 때문)
        isOn = isOn ? false : true
    }
}

var big = BigSwitch()
print(big.isOn) //false
big.toggle()
print(big.isOn) //true

 

728x90