chaneeii / iOS-Study-Log

✨ iOS에 대해 공부한 것들을 기록합니다 ✨
19 stars 0 forks source link

Protocol (프로토콜) #13

Open chaneeii opened 2 years ago

chaneeii commented 2 years ago

Protocol

Protocol 이란

"A protocol defines a blueprint of methods, properties, and other requirements that suit a particular task or piece of functionality."

프로토콜은 특정 작업 혹은 기능들을 구현하기 위한 메소드, 프로퍼티 그리고 기타 다른 요구사항들의 청사진이다. 프로토콜은 스위프트에서 자바의 interface와 비슷한 역할을 한다.

Syntax

protocol Person { func sleep() }

// 프로토콜을 채택한다. protocol Student: Person { func sleep() { } }

## Class-Only Protocol
- AnyObject : 클래스만 채택가능한 프로토콜을 만들어줌

```swift
protocol Person {
    func sleep()
}

//Class-Only Protocols
protocol PersonObject: **AnyObject**, Person {

}

// ✅ Class-Only Protocols 채택
class Student1: PersonObject {
    func sleep(){
        print("클래스는 채택가능")
    }
}

// ⚠️ Non-class type 'Student2' cannot conform to class protocol 'PersonObject'
struct Student2: PersonObject {
    func sleep(){
        print("struct은 채택 불가능")
    }
}

프로퍼티 요구사항

프로토콜에 선언되는 프로퍼티는 초기값을 할당할 수 없다. 따라서 var 키워드를 이용하여 선언되고 get 과 set이 가능하다.

protocol SomeProtocol {
   var settableProperty: String { get set }
   var notNeedToBeSettableProperty: String { get }
}

protocol AnotherProtocol {
    static var someTypeProperty: Int { get set }
    static var anotherTypeProperty: Int { get }
}

SomeProtocol 프로토콜을 정의하고, 변수로 두 개의 프로퍼티를 선언했다. settableProperty은 읽기/쓰기 모두 가능한 프로퍼티, notNeedToBeSettableProperty은 읽기만 가능하다.

AnotherProtocol은 타입 프로퍼티인데 반드시 static 키워드를 붙여 정의해야 한다.

클래스의 타입 프로퍼티에는 상속 가능한 타입 프로퍼티인 class 타입 프로퍼티와 상속 불가능한 static 타입 프로퍼티가 있는데, 이 두 타입 프로퍼티를 따로 구분하지 않고 모두 static 키워드를 사용하여 타입 프로퍼티를 요구하면 된다.

  • static : 오버라이딩 금지
  • class : 오버라이딩 허용

메서드 요구사항

프로토콜에서 메서드 작성법

protocol SomeProtocol {
    func someMethod() // 중괄호와 method 본문없어도 됨
    func anotherMethod(name: String, age: Int) -> Int
    func protocolMethod() -> String
}

타입메서드 요구사항

// 프로토콜 정의
protocol SomeProtocol {
    static func someTypeMethod()
    static func anotherTypeMethod()
}

// 구현부
class SomeClass: SomeProtocol {
    static func someTypeMethod() {

    }
    class func anotherTypeMethod() { // ✅자식 클래스에서 재정의 가능

    }
}

// ✅자식 클래스에서 재정의 가능
class SubClass: SomeClass {

   override static func anotherTypeMethod() {

   }
}

Mutating : 가변메서드 요구사항

가끔은 메서드가 인스턴스 내부의 값을 변경할 때가 있다.

protocol Togglable {
    mutating func toggle()
}

enum OnOffSwitch: Togglable {
    case off, on
    mutating func toggle() {
        switch self {
        case .off:
            self = .on
        case .on:
            self = .off
        }
    }
}
var lightSwitch = OnOffSwitch.off
lightSwitch.toggle()
// lightSwitch is now equal to .on

구조체, 열거형은 값 타입 이기 때문에 메서드 안에서 프로퍼티 값을 변경하지 못하지만, mutating 키워드를 사용하여 그것을 가능하게 해 준다. 참조형인 클래스에서는 mutating 키워드가 필요하지 않다.

protocol SomeProtocol{
    mutating func SomeMethod(_ num : Int)
}

// 값타입 -> mutating 으로 메소드안에서 프로퍼티 값 변경
struct SomeStruct : SomeProtocol{
    var x = 0
    mutating func SomeMethod(_ num :Int) {
        x += num
    }
}

// 참조형 -> mutating 필요 X 
class SomeStruct : SomeProtocol{
    var x = 0
    func SomeMethod(_ num :Int) {
        x += num
    }
}

이니셜라이저 요구사항

// 정의 : 매개변수만 지정
protocol Named {
    var name: String { get }

    init(name: String)

}

// 구현
struct Student: Named {
    var name: String

    init(name: String) {
       self.name = name
    }
}

Protocol 을 채택한 Class의 Initializer 구현

프로토콜을 채택한 타입은 Initializer를 designated initializer나 convenience initializer로 구현할 수 있습니다.

/ required 키워드가 없어도 Compile Error 가 발생하지 않는다./ final class myClass: SomeProtocol { init(number: Int) { ... } }

### Failable Initializer 요구사항
-  init?() 이나 init!()으로 선언된 생성자를 init() 으로 구현하는 것은 허용
   (프로토콜에 선언된 failable initializer를 conforming type이 non-failable로 구현해도 프로토콜 요구조건은 충족됩니다.)
- init()으로 선언되었다면 init() 이나 init!() 으로 구현되어야한다는 것
   (반대로 non-failable initailizer는 non-failable 이거나 IUO failable initializer로 구현해야 프로토콜 요구조건이 충족됩니다. )
```swift
protocol Something {
  init?()
}

struct Some: Something {
  init() {
  }
}

Protocol as Type

프로토콜은 First Class Citizen (일급시민) 이기 때문에, 하나의 독립적인 타입 ( a fully fledged types ) 으로 사용할 수 있다.

  1. function, method, initializer 의 parameter 와 return type으로 사용 가능
  2. constant, variable, property 의 type 으로 사용 가능
  3. array, dictionary 와 같은 컨테이너의 item의 type으로 사용 가능
    • 그리고 타입이기 때문에 Upper Camel Case 사용

Protocol Conformance

프로토콜을 채용하고 있는지 확인하는데에는 타입캐스팅 연산자를 사용합니다. ( is, as, as?, as! )

프로토콜 조합

하나의 매개변수가 여러 프로토콜을 모두 준수하는 타입이어야 한다면 조합하여 사용 가능 → SomeProtocol & AnotherProtocol 처럼 조합하여 표현한다

protocol Named{
    var name: String { get }
}

protocol Aged{
    var age: Int { get }
}

func celebrateBirthday(to celebrator: Named & Aged){
    print("Happy bday \(celebrator.name)!! You are \(celebrator.age)")
}

Delegation 을 위한 프로토콜

Delegation : 클래스나 구조체가 자신의 책임이나 임무를 다른 타입의 인스턴스에게 위임하는 디자인 패턴 ex. UITableView 타입의 인스턴스가 해야 하는 일을 위임받아 처리하는 인스턴스는 UITableViewDelegate 프로토콜을 준수하면 됨

(추가) 서브클래싱 vs 서브타이핑

상속하는 방식에 따라 이 두개의 차이가 갈린다.

https://epicdevsold.tistory.com/177 https://taekki-dev.tistory.com/38

Reference