iOS/Swift

Swift) Protocol 이해하기 (2/6) - 프로퍼티 / 메서드 선언 이해하기

소들이 2022. 10. 26. 20:45

 

 

 

안녕하세요!? 소들입니다 ☘️

저번 포스팅에서 프로토콜이 무엇인지에 대해 간단하게 공부했잖아요!?!?!

이번엔 이 프로토콜을 프로퍼티, 메서드로 선언할 때의

여러 가지 특징(?)에 대해 공부를 해보려고 해요!!!

모든 포스팅은 편의 말투로 합니다~!!

 

 

 

 

1. 프로토콜에서 프로퍼티 선언하기

 

 

protocol Band {
    var drum: String   { get set }
    var vocal: String  { get set }
    var piano: String  { get set }
    var guitar: String { get set }
}

 

 

위처럼 Band라는 것에 대해 일종의 약속, 즉 프로토콜을 만들었잖음?

앞으로 밴드는 이러이러한 속성(보컬, 피아노, 드럼, 기타)는 꼭 필요해요!! 하고!!

 

위처럼 필요한 속성에 대해 프로토콜 내부에 프로퍼티로 선언을 해두는데,

이때으 ㅣ특징에 대해 알아보겠음

일단 프로퍼티가 4개나 되어 넘 많우ㄴㅣ..........

피아노 프로퍼티 1개로 살펴 봅시다

 

 

 

1-1. 프로토콜을 채택해서 선언된 프로퍼티를 구현할 경우, 저장 프로퍼티로 구현하든, 연산 프로퍼티로 구현하든 상관 없다

 

 

우리가 쉽게 오해할 수 있는 것 중 하나가,

프로토콜에 선언되어 있는 프로퍼티는 항상 저장 프로퍼티로 구현해야 한다!! 인데,

 

 

class ABandBand {
    var piano: String = "sodeul"    // piano 프로퍼티를 저장 프로퍼티로 정의
}
 
class BBandBand {
    var piano: String {             // piano 프로퍼티를 연산 프로퍼티로 정의
        return "sodeul"
    }
}
 

 

 

위처럼 Band라는 프로토콜에 선언되어 있는 같은 piano 프로퍼티를 구현하더라도,

저장 프로퍼티 / 연산 프로퍼티와 상관 없이 구현할 수 있음!

 

 

 

1-2. 프로토콜에 선언되는 프로퍼티는 항상 var로 선언 되어야 한다

 

 

protocol Band {
    var piano: String { get set }
}

 

 

잠깐 다시 프로토콜 선언으로 돌아가서 보자면,

위처럼 프로토콜 내에 프로퍼티를 선언할 땐 반드시

var로 선언해주어야 함!!!

 

만약 let으로 선언해주면,

 

 

 

 

이렇게 에러가 뜨면서 var로 바꾸라고 나옴!!!!

 

프로토콜의 프로퍼티는 채택하는 곳에서 저장 / 연산 상관없이 구현 가능한데,

이때 연산 프로퍼티는 반드시 var로 선언해야 하기 때문에 (연산 프로퍼티의 경우 let 선언 자체가 불가)

만약 프로토콜에서 let으로 선언해두면, 구현하는 곳에서 연산 프로퍼티로 사용할 수 없어,

var가 필수로 요구되는 것이 아닐까 싶음(에러 메서지 내용도 그렇고!!)

 

에엥  그러면 해당 프로토콜을 채택하는 곳에선 항상

프로토콜에 선언된 프로퍼티를 var로 선언해야 하나요??

 

라는 의문이 들 수 이씀니다

이 질문에 대한 답은 바로바로!!!!!!!!!!!

 

연산 프로퍼티는 당연히 var로 선언해야 된다! (let은 원래 불가하다 닝겐!)

그러나 저장 프로퍼티의 경우, 타입 뒤에 달려 있는 { get set }에 따라 달려있다!

 

입니다 XD

이해가 안 갈테니 이해를 하러 가봅시다!

 

 

 

1-3. { get set } 속성에 따라 달라지는 let / var 속성

 

여러어부운

{ get set }의 속성에 대해 알아봅시다아

근데, 머라거??

 

저장 프로퍼티는 { get set }에 따라서 let / var의 속성이 달라질 수 있습니다

근데 연산 프로퍼티는 오로지 var만 가능하기 때문에 var만 사용해야 됩니다

 

 

를 이해 했다면 매우 쉬움ㅋ

 

 

1️⃣ { get }

 

저장 프로퍼티의 경우 let / var선언 모두 가능 합니다

연산 프로퍼티의 경우 getter(get-only) / getter & setter 선언 모두 가능 합니다

 

 

protocol Band {
    var piano: String { get }
}

 

 

위처럼 get으로 선언할 경우, 

실제 채택하는 곳에서 저장 프로퍼티로 구현할 경우 

let, var로 선언하든 상관 없음!!! 둘 다 가넝!!!

 

 

class ABandBand {
    let piano: String = "sodeul"          
}
 
class BBandBand {
    var piano: String = "sodeul"          
}
 

 

 

이렇게, let으로 선언해도 / var로 선언해도 문제 없단 말임!!

연산 프로퍼티로 구현할 경우

 

 

class ABandBand {
    var random: String = ""
    var piano: String {
        get {
            return "sodeul"
        }
    }
}
 
class BBandBand {
    var random: String = ""
    var piano: String {
        get {
            return "sodeul"
        }
        set {
            self.random = newValue
        }
    }
}
 

 

 

위처럼 getter만 만들어서 get-only로 만들어도,

getter setter모두 만들어서 둘 다 사용하든 문제 없음!!!

 

 

2️⃣ { get set } 

 

저장 프로퍼티의 경우 var로만 선언 가능합니다

연산 프로퍼티의 경우  getter & setter로 선언해주어야 합니다

 

 

protocol Band {
    var piano: String { get set }
}

 

 

위처럼 프로토콜 선언부의 프로퍼티를 { get set }으로 해줄 경우,

실제 채택하는 곳에서 저장 프로퍼티로 구현할 경우 

무조건 var로만 선언할 수 있고, let으로는 선언할 수 없음

 

 

class ABandBand {
    let piano: String = "sodeul"          // error! Type 'ABand' does not conform to protocol 'Band'
}
 
class BBandBand {
    var piano: String = "sodeul"          
}
 

 

 

위처럼 저장 프로퍼티를 let으로 선언할 경우, Band 프로토콜을 따르고 있지 않다고 에러가 남!

따라서 { get set } 으로 선언 될 경우, 반드시 구현하는 쪽에서 저장 프로퍼티는 var로 선언 해주어야 함!

 

연산 프로퍼티로 구현할 때도 마찬가지로,

 

 

class ABandBand {    
    var random: String = ""
    var piano: String {      // error! Type 'ABand' does not conform to protocol 'Band'
        get {
            return "sodeul"
        }
    }
}
 
class BBandBand {
    var random: String = ""
    var piano: String {
        get {
            return "sodeul"
        }
        set {
            self.random = newValue
        }
    }
}
 

 

 

 

{ get set } 일 경우, getter setter 모두 제공하는 것이 필수임!

따라서 위처럼 getter만 제공하는 경우(get-only), 에러가 발생함! setter만들어! 라는 ㅎ;ㅎ;;

 

 

 

 

1-4. 어떤 프로퍼티는 opional로 설정하고 싶은데요

 

하면 이전 포스팅에서 말했듯이

 

 

@objc protocol Band {
    var piano: String  { get set }
    @objc optional var base: String { get set }
}

 

 

이렇게 @objc를 붙여서 @optional로 선언해주면

채택해주는 곳에서 꼭 선언해주지 않아도 에러가 안 남

 

 

 

 

2. 프로토콜에서 메서드 선언하기

 

 

protocol Band {
    func play()
}

 

 

프로토콜에서 메서드의경우,

함수의 헤더 부분만 위처럼 작성해두고,

바디 부분은  해당 프로토콜을 채택하는 곳에서 직접 구현해야 된다고 했잖음!??!

 

 

class ABandBand {
    func play() {
        print("ABand!")
    }
}

 

 

이런 식으루!!! 메서드의 선언은 매우 간단함 :)

 

아.. 근디

 함수의 헤더 부분을 프로토콜의 선언해 둔다는 게 조금 어렵게 와닿을 수 있는데..

함수 타입 선언임니까...? 함수 표기법임니까...? 먼디요..? 싶을 수 읶ㅆ는디..

그냥.... 우리 그냥 메서드 선언할 때 있잖음..? 그때 { } 이거 뺀 부분 선언하는 것임...

 

 

 

 

이렇게..ㅎ_ㅎ...

어렵게 생각하지 마셈 ㅎ_ㅎ..

 

 

 

2-1. 구조체의 경우, mutating이 필요하면 프로토콜 자체에 추가

 

만약, 구조체의 경우 메서드 안에서 프로퍼티 값을 변경해야 할 경우

반드시 mutating이란 키워드를 func 앞에 붙여줘야 하잖음?? 

 

이때는, mutating도 같이 프로토콜에 선언해주면 됨!!

 

 

protocol Renameable {
    mutating func changeName(newName: String)
}
 
struct SodeulRenameable {
    var name: String = "sodeul"
    
    mutating func changeName(newName: String) {
        self.name = newName
    }
}
 
class DeulSo: Renameable {
    var name: String = "sodeul"
    
    func changeName(newName: String) {
        self.name = newName
    }
}

 

 

이렇게!!

이때 위처럼 프로토콜 메서드 자체에 mutating이 붙어 있는 경우,

구조체의 경우는 반드시 changeName이란 메서드를 선언할 때 mutating을 붙여야 하고,

클래스의 경우엔 mutating 키워드가 필요 없으니 떼고 선언해 주면 됨!!!

(위 코드에서 구조체의 경우 mutating이 붙지 않을 경우, 아예 다른 함수로 인식하기 때문에 에러가 뜸)

 

 

 

 

 

 

 

 

 

.

.

.

 

후움.. 생각보다 길어져서 생각보다 포스팅이 늦어졌네요 ㅠ_ㅠ...

만약 잘못된 내용이나 오타 및 피드백 댓글 주세요!!!