Swift) Protocol 이해하기 (3/6) - 프로토콜도 1급 객체다!
안녕하세요 :) 소들입니다 😎
프로토콜도 얼른 끝내버리기 위해 돌아왔다고 할 수 있습니다
프로토콜 글이 여태껏 제가 쓴 글 중 조회수가 제일 빠르게 오르더라구요..? 🥺
다들 프로토콜 덕후인듯.. 사실 나는 덕후 맞음..
Swift는 프로토콜 지향 프로그래밍이잖아여??
그래서 저는 요즘 코드를 짤 때 프로토콜 지향적으로 짜기 위해 매우 노력하는데..
늘 이 코드가 최선일까!?!에 대해 괴로워 하는 일개미 개발자..랄까 🐜 ..
쨌든 이번 포스팅에서는!
1급 객체로서의 프로토콜에 대해 공부해보겠습니다!!
쉬운 내용이니, 한번 공부하고 머릿속에 꼭 기억해두시길 바라겠습니다!
모든 포스팅은 편의 말투로 합니다~!!
1. 1급 객체로서의 프로토콜
먼저.. 1급 객체가 무엇이냐 묻는다면..
1급 객체 함수를 다룰 때 등장했으나!!!
언젠가의 포스팅이 보러가기 귀찮은 분들을 위해 대충 정리 해보겠음
일급 객체의 조건은 뭐 객체가 런타임에도 생성되어야 하고,
파라미터로 객체를 전달할 수 있고, 반환 값으로 객체를 사용할 수 있고 등등 있는데,
그중 프로토콜이 일급 객체가 되기 위해선 다음과 같은 조건들을 만족해야함
변수/상수의 타입이 프로토콜이 될 수 있어야 하고,
함수의 파라미터 타입과 반환 타입으로 프로토콜이 될 수 있어야 한다
고로,
어떠한 객체(object)가 독립적인 하나의 타입으로 사용될 수 있다면 이 객체는 1급 객체임!!
Swift에선 함수, 클로저, 프로토콜이 바로 1급 객체임
자 이제 1급 객체에 대해서 좀 이해를 했다면!!
아항 프로토콜은 독립적인 하나의 타입으로 볼 수 있구나!!!를 이해 했다면!!
사실 포스팅 끝ㅋ
이나.. 실제로 예를 통해 봅시다..
protocol Band {
var piano: String { get }
}
|
위와 같은 프로토콜이 있다고 가정해보자!!
1-1. 프로토콜 타입을 변수/상수 타입으로 선언할 수 있다
let beatles: Band
|
위처럼 beatles란 변수의 타입으로, 프로토콜을 선언할 수 있다는 말임!!!
근데 여기서 이제 프로토콜 초보들은 말이여..
헷갈리기 시작할 것임
아닣..
근데 그러면.. 프로토콜을 변수의 타입으로 선언하면..
프로토콜은 구조체도 클래스도 열거형도 아니라 인스턴스 생성도 못하는데요..
프로토콜은.. 야레야레.. 약간 ... 추상적.. 이랄까..?
선언부만 가진 놈 아닌가요..?
실제 구현은 프로토콜을 채택하는 구조체/클래스/열거형에서 한다면서..
그럼 위처럼 변수 타입을 프로토콜로 설정해버린 경우는 어떻게 객체를 생성하죠..?
싶을 수 있잖음!!
맞음!! 프로토콜은 클래스도, 구조체, 열거형도 아니기 때문에 해당 프로토콜의 인스턴스를 생성한단 것은 말이 안 됨
따라서 위처럼, 프로토콜을 타입으로 쓴다는 말은,
해당 프로토콜을 채택한 구조체/클래스/열거형의 인스턴스를
프로토콜로 타입 캐스팅을 해서 사용하겠다!
라는 말이라고 보면 됨!!
자, 만약 아래와 같이 Band라는 프로토콜을 채택하는
struct Beatles: Band {
var piano: String = "sodeul"
var bandName: String = "Beatles"
}
|
Beatles라는 구조체가 있음
이 Beatles는 Band라는 프로토콜을 채택했기 때문에,
반드시 구현해줘야 하는 piano란 프로퍼티를 위와 같이 선언하고,
내 내부에서 사용하고 싶은 bandName이란 프로퍼티또한 별도로 선언 했음!!
그러면!!!
let beatles: Band = Beatles.init()
|
이렇게 1급 객체 특징에 의해 Band라는 상수의 타입으로 프로토콜을 선언했을 때,
이 Beatles의 인스턴스를 이 프로토콜 타입을 가진 변수 beatles에 대입할 수 있다는 것임
어떻게 이게 가능하냐구?
Beatles라는 구조체 인스턴스를, 프로토콜 타입으로 "타입 캐스팅" 해버리기 때문에 가능한 거임!!
(타입 캐스팅이 어려운 스린이는 이 글을 먼저 봐주세요!)
따라서, 이때 beatles는
Beatles란 구조체의 인스턴스를 생성해서 대입해 줬지만,
타입 자체가 Beatles란 구조체 타입이 아닌, Band라는 프로토콜 타입이기 때문에
프로토콜에 선언되어 있는 piano라는 프로퍼티엔 접근할 수 있지만,
Beatles 구조체에 선언해줬던 bandName이란 프로퍼티엔 접근 할 수 없음!
타입 캐스팅을 알고 있다면, 어렵지 않을 것 :)
물론, 타입 캐스팅이기 때문에
만약에 Band란 프로토콜을 채택하지 않다면
as로 동작하는 위 코드의 경우
(아무 프로토콜도 채택하지 않은 BBand의 인스턴스를 캐스팅 함)
BBand는 Band를 따르지 않는다며 컴파일 에러가 발생하지만,
만약, as?나 as!를 써서 런타임까지 가게 될 경우,
as?로 캐스팅 할 경우엔 해당 상수는 nil로 떨어지고,
as!로 캐스팅 할 경우엔 크래쉬가 발생한답니당..!!
참고로, 위처럼 프로토콜로 타입 캐스팅된 인스턴스를
다시 기존 타입으로 캐스팅할 경우엔, 무조건 as?, as! 만 가능하답니다 XD
프로토콜의 경우, 기존 인스턴스가 어떤 타입인지에 대해선 전혀 1도 관심 없고
너 이 프로토콜 채택했으니 이 약속은 지켰겠지? 이 프로퍼티, 메서드는 당연히 갖고 있겠지?
하고 그 프로토콜에 선언된 놈들만 쓰기 위한 용도로 캐스팅 해버렸기 때문에
기존 인스턴스 타입이 어떤지에 대한 정보가 1도 없기 떄문에
타입 캐스팅에 성공할지 여부에 대해서 알 수가 없어서
실제 런타임에 확인해야만 알 수 있어서인듯 함..!!!
정리하자면,
인스턴스를 프로토콜로 타입으로 캐스팅 할 땐 as, as?, as! 모두 가능하지만,
프로토콜로 타입 캐스팅 된 인스턴스를 원래 타입으로 캐스팅 할 땐 런타임에 확인하는 as?나 as!만 가능합니다
(물론 잘못된 타입으로 캐스팅 할 경우 크래쉬 발생)
1-2. 프로토콜 타입을 함수의 파라미터 타입과 반환 타입으로 사용할 수 있다
우린 위의 포스팅을 이해했다면
아하ㅋ 함수의 파라미터 타입과 반환 타입을 프로토콜 타입으로 선언할 수 있단 말은 말이죠ㅋ
프로토콜은 자체적으로 인스턴스를 생성할 수 없기 때문에
해당 프로토콜을 준수하는 구조체/클래스/열거형 인스턴스를
프로토콜 타입으로 캐스팅해서 전달한다는 거군요 ㅋ
라는 결론이 도달 했다면 포스팅 나가세요ㅕ
하산하셈
func doSomething(band: Band) -> Band {
return Beatles.init()
}
|
위처럼 Band란 프로토콜 타입을 파라미터 band의 타입으로 지정해줄 수 있고
리턴 타입으로 Band란 프로토콜 타입을 지정해줄 수 도 있음!!!
당연히, 프로토콜은 어떤 인스턴스를 생성할 수 있는 객체는 아니기 때문에
위 코드에서 return문을 보면
Band란 프로토콜을 채택하고 있는 Beatles 구조체의 인스턴스를
Band란 프로토콜 타입으로 타입 캐스팅해서 리턴한단 말이고,
let beatles = Beatles.init()
doSomething(band: beatles)
|
실제 함수를 호출하는 곳에서도
Band란 프로토콜을 채택한 Beatles 구조체의 인스턴스를
Band란 프로토콜 타입으로 타입 캐스팅 해서 파라미터로 보낸다는 뜻임!!!
이 타입 캐스팅이란 것을 처음 말했던 "약속"이란 측면에서 보면 이해가 쉬움
너 Band란 프로토콜 준수하네!? 그러면 반드시 piano란 프로퍼티 갖고 있겠지!?!?
나 너가 다른 어떤 프로퍼티 / 메서드 선언했는진 관심 없어!
너가 Band란 약속으로 인해 구현해놓은 piano란 프로퍼티에 접근한다!!
라는 뜻으로! :)
2. 프로토콜 적합성
마지막으로, 우리가 타입 캐스팅에서 대해 얘기했으니
프로토콜 적합성이란 것에 대해 쉽게 이해하고 가겠음
2-1. is : 해당 인스턴스는 이 프로토콜을 채택하고 있니?
protocol Band { }
protocol Solo { }
struct Beatles: Band { }
let beatles = Beatles.init()
beatles is Band // true
beatles is Solo // false
|
위처럼 is를 사용하면,
인스턴스가 해당 프로토콜을 채택하고 있는지 아닌지를 Bool(true / false)값으로 리턴해줌!!!
.
.
.
프로토콜 3번째 포스팅도 끝!!!
월요일은 힘들군요 ㅠㅠ....
오타나 잘못된 내용 있으면 언제든 댓글 주세여!!