# 중첩타입 개념
타입 내부에 타입을 선언하는 것을 중첩타입이라고 한다. 중첩 타입의 선언은 언제나 가능하다.
class Aclass {
struct Bstruct {
var name: Cenum
}
}
// 타입 선언과 인스턴스의 생성
let aClass: Aclass = Aclass()
// 멤버와이즈 이니셜라이저 호출
let bStruct: Aclass.Bstruct = Aclass.Bstruct(name: .bCase)
중첩타입을 사용하는 이유는 아래와 같다.
- 타입간의 연관성에 따라 특정 타입 내에서만 사용하기 위해서
- 타입간의 연관성에 따라 내부 구조를 디테일하게 설계
struct BlackjackCard {
// Suit(세트) 열거형 - 중첩
enum Suit: Character { // 원시값(rawValue)사용
case spades = "♠", hearts = "♡", diamonds = "♢", clubs = "♣"
}
// 순서(숫자) 열거형 - 중첩
enum Rank: Int { // 원시값(rawValue)사용
case two = 2, three, four, five, six, seven, eight, nine, ten
case jack, queen, king, ace // (원시값 존재하지만 사용하지 않고자 함 ===> values)
// Values 타입정의 (두개의 값을 사용) //===> 열거형 값(순서)을 이용 새로운 타입을 반환하기 위함
// 기본 지정 생성자 제공
struct Values {
// 에이스 카드의 두 가지 역할로 인해 second라는 속성이 생긴것
let first: Int, second: Int?
}
// (읽기) 계산 속성 (열거형 내부에 저장 속성은 선언 불가)
var values: Values {
switch self {
case Rank.ace:
return Values(first: 1, second: 11) // 에이스 카드는 1 또는 11 로 쓰임
case .jack, .queen, .king:
return Values(first: 10, second: nil) // 10으로 쓰임
default:
return Values(first: self.rawValue, second: nil) // 2 ~ 10까지의 카드는 원시값으로 쓰임
}
}
}
// 블랙잭 카드 속성 / 메서드 =======================================
// 어떤 카드도, 순서(숫자)와 세트(Suit)를 가짐
let rank: Rank, suit: Suit
// (읽기) 계산속성
var description: String {
get {
var output = "\(suit.rawValue) 세트,"
output += " 숫자 \(rank.values.first)"
if let second = rank.values.second { // 두번째 값이 있다면 (ace만 있음)
output += " 또는 \(second)"
}
return output
}
}
}
개발 단계에서 좀 더 시맨틱한 개발을 위해 사용한다고 보면 된다. 아래 두 코드를 비교해보자
enum Style {
case full
case long
case medium
case none
}
struct DateFormatters {
var style: Style
}
var dateStyle1 = DateFormatters(style: .full)
dateStyle1 = DateFormatters(style: Style.full)
dateStyle1.style = Style.full
dateStyle1.style = .full
위 코드는 Style
열거형을 DateFormatters
구조체로부터 분리하여 코드를 작성하였다. 이렇게 되면 변수 선언과 타입 작성에는 더 편하지만 dateStyle1.style = Style.full
의 경우 어떤 스타일에 대한 이야기인지 명확하게 파악하기가 어렵다.
아래 코드를 다시 봐보자.
struct DateFormatters {
var style: DateFormatters.Style
// 중첩타입으로 선언
enum Style {
case full
case long
case medium
case none
}
}
var dateStyle2 = DateFormatters(style: .full)
dateStyle2 = DateFormatters(style: DateFormatters.Style.full)
dateStyle2.style = DateFormatters.Style.full
dateStyle2.style = .long
세 번째 코드의 경우 dateStyle2.style = DateFormatters.Style.full
를 보면 타입에 대해 명확히 지정해줌으로써 dateStyle2
라는 객체 내의 style
속성이 어떤 타입과 연관되어 있는 것인지 명확하게 파악이 가능하다.
중첩타입 파악하기
DateFormatter.Style.full
과 같이 중간에 대문자로 시작하는 접근 연산이 나타나면 중첩타입임을 알아채자- 실제 앱을 제작할때 중첩 타입을 활용하여 타입 간의 연관성을 명확하게 하자.
# 예시
struct K {
static let appName = "MySuperApp"
static let cellIdentifier = "ReusableCell"
static let cellNibName = "MessageCell"
static let registerSegue = "RegisterToChat"
static let loginSegue = "LoginToChat"
struct BrandColors {
static let purple = "BrandPurple"
static let lightPurple = "BrandLightPurple"
static let blue = "BrandBlue"
static let lighBlue = "BrandLightBlue"
}
struct FStore {
static let collectionName = "messages"
static let senderField = "sender"
static let bodyField = "body"
static let dateField = "date"
}
}
// 문자열 대신에 아래처럼 사용할 수 있음 ⭐️ (문자열 실수는 치명적인 에러를 발생시킴)
let app = K.appName // "MySuperApp"
let color = K.BrandColors.blue // "BrandLightBlue"
각종 데이터 상수화를 위해 중첩 타입을 사용한다.
아래 코드는 메세지 앱 제작 모델에 관련된 예시 코드이다. 지나치지 말고 복습할때 다시 읽어보기
class Message {
// 상태를 중첩타입으로 (외부에서 접근못하게 하려면, private으로 선언가능)
private enum Status {
case sent
case received
case read
}
// 보낸 사람, 받는 사람
let sender: String, recipient: String, content: String
// 보낸 시간
let timeStamp: Date
// 메세지 상태 정보 (보냄/받음/읽음)
private var status: Message.Status = Message.Status.sent
init(sender: String, recipient: String, content: String) {
self.sender = sender
self.recipient = recipient
self.content = content
self.timeStamp = Date() // 현재시간 생성 ===> 시간은 유저가 주는 정보 아님
}
func getBasicInfo() -> String {
return "보낸사람: \(sender), 받는사람: \(recipient), 메세지 내용: \(content), 보낸 시간: \(timeStamp.description), "
}
// 메세지 상태에 따라서, 색깔 바뀜
func statusColor() -> UIColor {
switch status {
case .sent:
return UIColor(red: 1, green: 0, blue: 0, alpha: 1)
case .received:
return UIColor(red: 0, green: 0, blue: 1, alpha: 1)
case .read:
return UIColor(red: 0, green: 1, blue: 1, alpha: 1)
}
}
}
let message1 = Message(sender: "홍길동", recipient: "임꺽정", content: "뭐해?")
print(message1.getBasicInfo())
sleep(1)
let message2 = Message(sender: "임꺽정", recipient: "홍길동", content: "그냥있어")
print(message2.getBasicInfo())