반응형
LinkedIn 개발자로 성장하면서 남긴 발자취들을 확인하실 수 있습니다.
Github WWDC Student Challenge 및 Cherish, Tiramisul 등 개발한 앱들의 코드를 확인하실 수 있습니다.
개인 앱 : Cherish 내 마음을 들여다보는 시간, 체리시는 디자이너와 PM과 함께 진행 중인 1인 개발 프로젝트입니다.
10년 후, 20년 후 나는 어떤 스토리 텔러가 되어 있을지 궁금하다. 내가 만약에 아직 조금 더 탐구하고 싶은 게 있고, 궁금한 게 있다면, 그게 설사 지금 당장의 내 인생에 도움이 안 되는 것 같더라도 경험해보자. 그 경험들을 온전히 즐기며 내 것으로 만들고, 내 일에 녹여내고... 그러다보면 그 점들이 모여 나란 사람을 그려내는 선이 될 테니까.

Recent Posts
Recent Comments
Total
관리 메뉴

꿈꾸는리버리

protocol 정복기 ( 2/ 3) 본문

오뚝이 개발자/swift

protocol 정복기 ( 2/ 3)

rriver2 2022. 7. 24. 09:42
반응형

이번 포스팅에서는 protocol에서 method나 init을 요구할 때에 대해 알아보고자 한다 !

protocol이 무엇인지와 protocol에서 property를 요구할 때 주의해야 하는 사항들에 대해 모른다면, 이전 포스팅을 보고 오는 것을 추천한다 !

 

🌷 protocol 요구사항 ( 이전 포스팅 이어서... ) 

2️⃣ method 요구

프로토콜은 특정 인스턴스 메서드나 타입 메서드를 요구할 수 있다. 이전에 말한 대로, protocol에서는 선언만을 해야 한다.

1) 따라서 protocol에서는 실제 구현부를 제외하고 매서드의 이름, 매개변수(가변 매개변수도 가능), 반환 타입 등을 작성해야 한다. 

2) 이때, 프로토콜에서는 method의 매개변수의 기본값은 지정할 수 없다.

 

 예시 코드 

protocol SomeProtocol {
	func anotherMethod(name: String, age:Int) -> Int
}

 

3) 프로토콜을 매개변수나  반환 타입, 또는 프로퍼티의 타입으로 지정할 수 있다.

프로토콜도 하나의 타입이기 때문에 메소드의 반환, 프로퍼티 타입이 될 수 있다.

 

 예시 코드 

원래라면 이렇게 같은 기능을 하는 printA, printB 함수더라도, 두 클래스가 다르기 때문에 두번이나 함수를 작성해야 한다.

class PersonA{
    var name: String = "hiA"
}
class PersonB{
    var name: String = "hiB"
}
func printA(person: PersonA) {
    print(person.name)
}
func printB(person: PersonB) {
    print(person.name)
}

let personA = PersonA()
let personB = PersonB()

printA(person: personA)
printB(person: personB)

 

하지만, PersonA와 PersonB가 name이라는 변수가 같기 때문에 하나의 Person protocol을 각각 채택한다고 했을 때, 함수를 좀 더 간결하게 적을 수 있다. 이때 함수의 매개변수에 Person이 쓰였음을 확인할 수 있다.

protocol Person {
    var name: String { get }
}
class PersonA: Person{
    var name: String = "hiA"
}
class PersonB: Person{
    var name: String = "hiB"
}
func printAB(person: Person) {
    print(person.name)
}

let personA = PersonA()
let personB = PersonB()

printAB(person: personA)
printAB(person: personB)

 

🌷 타입 매서드

타입 매서드의 경우에는 타입 프로퍼티와 마찬가지로 타입 매서드를 요구할 때는 static 키워드를 붙이고, 구현부에서 class를 사용할지, static을 사용할 지 선택하면 된다.

 

 예시 코드 

protocol SomeProtocol {
    static func someTypeMethod()
    static func anotherTypeMethod()
}

+) static과 달리 class의 경우에는 상속이 가능하다. 

class someClass : SomeProtocol{
    static func someTypeMethod() {  // code  }
    class func anotherTypeMethod() {  // code  }
}

class someChildClass : someClass{
    override static func anotherTypeMethod(){  // code  }
}

 

🌷 가변 메서드의 요구

가변 메서드는 우선, struct나 enum과 같이 값 참조일 경우에 인스턴스 method에서 자신 내부의 값을 변경해야 할 때 사용된다는 것을 알고 있어야 한다. method의 앞에 mutating이라는 키워드를 붙여 메서드에서 인스턴스 내부의 값을 변경한다는 것을 명시 한다. 따라서 주소 참조인 class의 경우에는 mutating이 필요 없다.

 

그렇다면 프로토콜에서는 어떨까 ? class에서 protocol을 채택하든, struct을 채택하든,.. 상관 없지 않는가...??

우선, 프로토콜에서는 mutating을 명시해서 인스턴스 내부의 값을 변경할 수도 있음을 표기한다. 그리고 구현체에서는 mutating을 값참조인 경우에만 표기한다.

 

 예시 코드 

protocol FullName {
    var name: String { get }
    mutating func changeName()
}

FullName 프로토콜의 요구 method인 changeName은 mutating method이다.

 

 

이렇게 struct인 경우에는 mutating임을 명시해야 하고,

struct PersonStruct: FullName{
    var name: String = "hi"
    mutating func changeName() {
        self.name = "이름 변경 : \(self.name)ss"
    }
}

 

class인 경우에는 mutating임을 명시하지 않아도 된다.

class PersonClass: FullName{
    var name: String = "hi"
    func changeName() {
        self.name = "이름 변경 : \(self.name)ss"
    }
}

 

+) 신기했던 거는 프로토콜에서 가변 메서드로 명시했다고 하더라도, struct 구현체에서 내부의 property값을 변경시키지 않는다면, mutating을 붙이지 않아도 된다는 것이다 ㅎㅎ

struct PersonStruct: FullName{
    var name: String = "hi"
    func changeName() {
        print("hi")
    }
}

 

 

3️⃣ Initializer 요구

자 ,, 이제 드디어 init 요구이다 !!

protocol에서는 해당 protocol을 준수하려는 타입에게 특정한 이니셜라이저를 구현하도록 요구할 수 있다.

이번에도 마찬가지로 init의 매개변수를 지정하기만 할 뿐 init 구현은 실제 구현체에서 하게 된다.

 

 예시 코드 

protocol SomeProtocol {
	var name: String { get }
    
    init(name: String)
}

이렇게 protocol에서 요구한 init은 구현체가 상속이 가능한 class의 경우에는 구현체에서 required 식별자를 붙여야 한다.

 

즉, 다시 말해 final class가 아닌 class는 required 식별자를 붙여서 해당 class를 상속 받는 클래그세 해당 이니셜라이저를 모두 구현해야함을 명시해야 한다.

만약 required를 안 붙인다면 이런식으로 오류가 뜬다.

 예시 코드 

protocol SomeProtocol {
    var name: String { get }
    
    init(name: String)
}

// 상속을 할 수 없는 struct
struct someStruct: SomeProtocol {
    var name: String
}

// 상속을 할 수도 있는 class
class someClass: SomeProtocol {
    var name: String
    required init(name: String) {
        self.name = name
    }
}

// 상속이 더 이상 필요 없는 final class
final class someClass2: SomeProtocol {
    var name: String
    init(name: String) {
        self.name = name
    }
}

 

 

이렇게 상속 가능한 class의 경우에는 그 하위의 서브 클래스에서도 init을 구현해줘야 한다. 기본적으로 required는 기본적으로 override를 포함하기 때문에 자식클래스에서 요구 이니셜라이저를 재정의 할 때는 override 대신 required 수식어 사용한다.

 

 예시 코드 

protocol SomeProtocol {
    var name: String { get }
    
    init(name: String)
}

class someClass: SomeProtocol {
    var name: String
    required init(name: String) {
        self.name = name
    }
}

class subClass : someClass {
    var age: Int
    init(age: Int, name: String) {
        self.age = age
        super.init(name: name)
    }
    required init(name: String) {
        self.age = 5
        super.init(name: name)
    }
}

 

하지만, 특정 클래스에 프로토콜이 요구하는 init을 이미 구현한 상황에서 그 클래스를 상속받은 클래스가 있다면, required와 override를 모두 명시해서 프로토콜에서 요구하는 init을 구현해줘야 한다.

 

 예시 코드 

protocol SomeProtocol {
    var name: String { get }
    
    init(name: String)
}

class someClass {
    var name: String
    init(name: String) {
        self.name = name
    }
}

class subClass: someClass, SomeProtocol {
    required override init(name: String) {
        super.init(name: name)
    }
}

 

추가적으로 protocol은 실패 가능한 init을 요구할 수도 있다. 이때 구현체에서는 실패 가능한 init으로 구현을 해도 되고 일반적인 init으로 구현을 해도 된다.

 

 예시 코드 

protocol SomeProtocol {
    var name: String { get }
    
    init?(name: String)
}

class someClass: SomeProtocol {
    var name: String
    required init!(name: String) {
        self.name = name
    }
}
class someClass2: SomeProtocol{
    var name: String
    required init(name: String) {
        self.name = name
    }
}

 

 

오늘은 protocol에서 method나 init을 요구할 때에 대해 알아봤다. 다음 포스팅에서는 프로토콜을 상속하는 경우, class 전용 프로토콜, 그리고 프로토콜의 조합과 프로토콜을 준수했는지 확인하는 등...의 프로토콜의 또 다른 부분을 다뤄보고자 한다. 

 

  참조  

https://docs.swift.org/swift-book/LanguageGuide/Protocols.html

 

Protocols — The Swift Programming Language (Swift 5.7)

Protocols 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 tho

docs.swift.org

 

반응형
Comments