all white cheat sheet Dana’s Swifty Life

Protocol in Swift : 자격요건 명시하는 역할

:pushpin: Table Of Contents

Protocol

  • 프로토콜을 사용하는 이유
  • 프로토콜 상속(X) / 채택, 준수(O)
  • Protocol Requiremet
    • Property 명시하기 (Property Requirement)
    • Method 명시하기 (Method Requirement)
    • Initializer 명시하기 (Initializer Requirement)
  • Protocol을 type처럼 사용할 수 있다 (Protocol as Type)
    • Collection element 로 사용하기
  • Protocol 준수하기 - extension 사용 (Adding Protocol Conformance with an Extension)
  • 프로토콜 여러 개 사용하기 (Protocol Composition)
  • Protocol 상속 (Protocol Inheritance)
  • Class-Only Protocol
  • Protocol 준수하는지 확인하기 (Checking for Protocol Conformance)
  • Delegation (역할 위임)
  • Optional Protocol Requirements
  • Protocol 확장하기 (Protocol Extensions)

 

 


Protocol

역할/기능에 대한 자격 요건을 미리 명시해 둔 것

A protocol defines a blueprint of methods, properties, and other requirements that suit a particular task or piece of functionality. The protocol can then be adopted by a class, structure, or enumeration to provide an actual implementation of those requirements. Any type that satisfies the requirements of a protocol is said to conform to that protocol.

  • 이 역할을 하려면 이런 자격 요건을 가져야 한다 = protocol 에 명시해둔다 (suit a particular task)

  • 이 type의 instance는 이런 역할을 할 수 있다는 명확한 명세, 조건

  • struct/class/enum 에서 protocol을 채택할 수 있다.

    • 채택하는 type에서는 protocol 에서 지정한 자격요건의 구체적인 구현을 제공해야 한다.
  • Syntax

    protocol NewProtocol {
    	var some: String { get set }
    	var another: Int { get }
    	func doSomething() -> Bool
    }
    

     

  • Example : 비서 공고에서 자격요건 명시하기

    • 공고에 ‘이 일을 하려면 ~이런이런 자격, 능력이 필요하다’ 라고 명시해둔다
    • 지원자는 해당 능력 가지고 있다고 써있는 이력서 가지고 지원한다.
      
    protocol Viseoable {  // 비서가 될 수 있는 사람은 이런 조건을 가져야 한다.
    	func manageSchedule()
    	func brewCoffee()
    	func drive()
    	var degree: String { get } //학위를 접근해서 확인해야 하니까
      var diverLicense: String { get }
    }
      
    struct Secretary: Viseoable {
    	func manageSchedule() { ...구현}
    	func brewCoffee() { }
    	func drive() { } 
      
    	var degree: String 
    	var diverLicense: String 
    }
      
    struct Assistant: Viseoable {
    	func manageSchedule() { ...뫄뫄구현}
    	func brewCoffee() { }
    	func drive() { } 
      
      var degree: String 
      var diverLicense: String
    }
      
    struct Sajang {
    	var viseo: Viseoable //이 protocol(자격) 가진 사람은 모두 여기 들어올 수 있다.
    }
      
      
    let sec1: Secretary = Secretary(degree: "뫄뫄대", driverLicens: "2종 보통")
    let sec2: Assistant = Assistant(degree: "뭐뭐대", driverLicens: "1종 보통")
      
    var master: Sajang = Sajang(viseo: sec1) // sec1은 비서 할 수 있다
    master.viseo = sec2 //sec2도 가능
      
    

     

  • 프로토콜 네이밍: 주로 *~를 할 수 있다(-able) 는 의미로 많이 지음

  • 프로토콜에서는 function, property 정의만 넣지 구현은 하지 않음

    • 조건만 명시해 줌
    • 구현(준수)은 해당 protocol 구현하는 struct, class, enum 에서
    • property와 extension을 같이 사용하면 protocol level에서 구현도 가능함 (뒤에 나옴)
  • 행위 자체의 구체적인 구현은 다를 수 있지만, protocol을 가진다는 것은 자격을 가진다는 것

  • protocol은 class 와는 다르게 여러개 채택이 가능하다

 

 


protocol 을 사용하는 이유

  • type에 상관 없이 이런 역할, 자격을 가진 instance를 받고 싶을 때!

  • type이 바뀌면 사용할 수 없는 케이스가 생길 수 있음

  • protocol(자격)을 가진 instance라면 모두 OK :ok_hand: (Protocol as Types)

  • type보다 좀 더 유연한 접근

  • 목적에만 부합하면 괜찮은 경우

  • 유연성, 확장성을 위해서

    • cf) 유연성 정도가 더 높은 것은 generic
  • Any type을 쓰면 안에서 원하는 타입인지 확인하는 과정이 필요

    → 프로토콜 사용시 그 과정 스킵 가능

  • function을 사용하는 모든 타입(class, struct, enum)이 protocol 채택 가능

  • 상속이 가능한 type은 class 뿐인데, value type인 struct, enum도 타입 확장 가능한 방법

 

 


protocol 상속(X) / 채택, 준수(O)

  • 타입 정의할 때 ‘protocol을 상속받는다’ 라고 :exclamation: 절대:exclamation: 쓰지 않는다

    • 이 protocol은 어떤 type에 의해 채택되었다 (adopted)

      (구현하는 type 관점)

    • 구현하는 type은 이 protocol을 준수한다 (conformed)

      (protocol 관점 혹은 protocol을 채택하려면 자격요건을 구현한 코드가 있어야 된다는 의미에서의 준수. rule을 잘 따르고 있는지의 여부.)

  • Example

    • struct Assistant 는 protocol Viseoable 을 채택하여 ~이런이런 method, property를 구현함으로써 해당 protocol을 준수한다.

      struct Assistant: Viseoable { //Viseoable 프로토콜을 채택했다
      	func manageSchedule() { ...뫄뫄구현} //이 프로토콜을 준수한다.
      	func brewCoffee() { }
      	func drive() { } 
          
      	var degree: String 
      	var diverLicense: String
      }
      

 

 


Protocol Requirement

Protocol 에서 자격요건으로 명시하는 것들

 

Property

protocol 에서 property 요구조건으로 명시하는 것들

  1. property 의 이름type
  2. instance / type property 도 명시해줄 수 있다.
    • type property : 앞에 static 붙임
  3. gettable & settable / gettable (read-only) 인지 명시해준다.
    • gettable & settable
    • stored constant property :x:
    • computed property :x: - gettable로만 명시되어있을 때, 구현에서 settable 로 수정해도 괜찮음

 

 

Method

  1. type / instance method

    • static or not
  2. mutating method

    mutating : instance를 수정하는 것이 허락된 method에 붙이는 keyword (alllowed to modify the instance)

    • value type의 method에서 사용된다 (struct/enum 에서만)
    • 선언에서 mutating 추가했으면 구현에서는 keyword 생략 가능

 

Example : toggle(두 가지 기능으로 전환하는 논리 스위치)

protocol Togglable {
	mutating func toggle()
}

enum OnOffSwitch: Toggleable {
  case off, on
  mutating func toggle() {
    switch self {
      case .off:
      	self = .on
      case .on:
      	self = .off
    }
  }
}

var lightSwitch = OnOffSwitch.off
lightSwitch.toggle()
// off -> on

 

 

Initializer

  1. conforming types(프로토콜을 준수할 type)에서 구현할 initializer도 명시할 수 있다.

    protocol SomeProtocol {
      init(someParameter: Int)
    }
    

 

  1. Class 에서 꼭 구현해야 하는 initializer

    required

    ( initializer 더 이해하고 나서 추후 update)

 

  1. Failable (실패 가능한) Initializer 도 명시 가능

    초기화 실패시 nil을 반환하는 initializer

 

 


Protocol 은 type 처럼 사용할 수 있다

Protocol as Types

  • protocol은 안에 실제 구현은 없지만 독립적으로 사용할 수 있는 type이다. (fully-fledged type)
  • type 사용되는 곳에 protocol도 사용 가능함
  • protocol을 사용하는 이유를 뒷받침 하는 특성
  • existential type : there exists a type T such that T conforms to the protocol

 

Example : class Dice

  • side가 몇개인지, n개 side 중 어떤 side가 나오는지 random number generator 를 가짐

  • RandomNumberGenerator protocol : 어떻게 random number를 생성하는지는 중요하지 않고, random() method를 구현하면 자격 충족

  • 어떤 type인지는 상관 없이 RandomNumberGenerator를 채택한 type의 instance라면 generator property에 들어갈 수 있다.

    class Dice {
      let numberOfSides: Int
      let generator: RandomNumberGenerator //protocol
        
      init(sides: Int, generator: RandomNumberGenerator) {
          self.sides = sides
          self.generator = generator
      }
        
      func roll() -> Int {
          return Int(generator.random() * Double(sides)) + 1
      }
    }
    
    var d6 = Dice(sides: 6, generator: LinearCongruentialGenerator())
      
    for _ in 1...5 {
      print("Random dice roll is \(d6.roll())")
    }
    

 

 


Protocol 준수하기 - extension

Adding Protocol Conformance with an Extension

  • extension 사용하면 이미 존재하는 type에 property, method, subscript 등 여러 기능을 추가할 수 있다
  • protocol requirement 도 extension을 사용해서 adopt & conform 할 수 있다.

 

Example : class Dice 가 protocol TextRepresentable 준수하도록 코드 작성

모든 Dice instance는 TextRepresentable를 준수한다

== TextRepresentable 의 instance 인 것처럼 처리될 수 있다.

protocol TextRepresentable {
	var textDescription: String { get }
}

extension Dice : TextRepresentable {
  var textDescription: String {
    return "A \(numberOfSides)-sided dice"
  }
}

let d12 = Dice(sides: 12, generator: LinearCongruentialGenerator())
print(d12.textualDescription)
// Prints "A 12-sided dice"

 

 


프로토콜 여러 개 사용하기 - &

Protocol Composition

  • 여러 개의 프로토콜을 동시에 준수할 수 있다. (a type is able ot conform to multiple protocols at the same time)

  • 하나의 requirement(자격을 원하는 곳) 에 &(ampersand) 사용하여 여러 개의 protocol을 합친 자격을 받을 수 있다.

  • 장점

    • 프로토콜을 세분화 할 수 있다

    • 일시적으로 두 protocol의 자격요건을 합친 protocol을 만든 것처럼 사용할 수 있다

      → 합친 새로운 protocol을 만들기는 비효율적일 때…

  • Example 1 : 사장의 비서는 운전과 커피를 만드는 능력이 필요하다

    protocol Drivable {
      var driverLicense: String
      func drive()
     }
      
    protocol Brewable {
      func brewCoffee()
    }
      
    struct Sajang {
      var viseo: Drivable & Brewable // protocol끼리 조합시엔 & 사용
    }
    
  • Example 2 : 이름과 나이를 가지는 instance 만 생일 축하해 줄 수 있다.

    protocol Named {
      var name: String { get }
    }
      
    protocol Aged {
      var age: Int { get }
    }
      
    struct Person: Named, Aged {
      var name: String
      var age: Int
    }
      
    func wishHappyBirthday(to celebrator: Named & Aged) {
      print("Happy B-day \(celebrator.name), you're now \(celebrator.age)")
    }
      
    let birthdayPerson = Person(name: "Dana", age: 26)
    wishHappyBirthday(to: birthdayPerson)
    // "Happ B-day Dana, you're now 26"
    

 

 


Protocol 상속 (Inheritance)

  • protocol도 다른 protocol을 채택할 수 있다. (한개 이상)

  • sub protocol은 super protocol에서 선언한 method, property 다 사용 가능하다 (상속의 특징)

    protocol Viseoable : Drivable, Brewable {
    		func manageSchedule()
    		var degree: String { get }
    }
    

 

 


Class-Only Protocol

Class만 채택하도록 한정하는 protocol

AnyObject protocol을 상속받으면 class-only protocol이 됨

class type만 이 protocol을 채택할 수 있다.

protocol ClassOnly: AnyObject, .. {

}

 

 


Protocol 준수하는지 확인하기

Checking for Protocol Conformance

  • Type Casting 사용해서 확인할 수 있다

  • keyword : is & as

    • target is someType : target이 이 type이 맞나요 → 맞으면 true, 아니면 false
    • target as?(!) someType : target을 someType으로 downcast (using with optional binding)
  • Example

    struct Point {
    	var x: Int, y: Int
    }
      
    struct Rect {
      var leftTop: Point
      var rightBottom: Point
    }
      
    protocol HasArea {
      var area: Double { get }
    }
      
    extension Rect: HasArea {
      var area: Double {
        return (rightBottom.x - leftTop.x) * (leftTop.y - rightBottom.y)
      }
    }
    
    let originPoint = Point(x: 0, y: 0)
    let myRect = Rect(leftTop: Point(x: 0, y: 10), rightBottom: Point(x: 10, y: 0))
      
    let objects: [AnyObject] = [originPoint, myRect]
      
    for object in objects {
      if object is Point {
      	print("Point (\(object.x), \(object.y))")
    	}
        
      if let rectangle = object as? Rect {
        print("Area of rectangle: \(rectangle.area)")
      }
    }
    

 

 


Delegation (역할 위임)

(Update later)


Optional Protocol Requirements

(Update later)


Protocol 확장하기 (Protocol Extensions)

(Update later)

protocol + extension = protocol extention

  • 특정 타입이 할 일 지정 + 구현을 한번에

 

protocol LayoutDrawable {
  func drawSomeLayout()
}

class MyView: UIView, LayoutDrawable {
  func drawSomeLayout() {
    ...
  }
}

 

  • Protocol Default Implementation: 프로토콜 기능을 미리 구현해 둔다.
    • 각 프로토콜에서 공통적으로 해야할 일은 protocol default implementation에서 설정해 놓으면 됨
protocol LayoutDrawable {
  func drawSomeLayout()
}

class MyView:UIView, LayoutDrawable {
	//….
}

extension LayoutDrawable { // Protocol Default Implementation
  func drawSomeLayout() {
    // draw some layout...
  }
}

 


Protocol Oriented Programming

(Update later)

 

  • swift - struct friendly language => 상속 불가 -> protocol

 

참조