🚀 열거형은 문자열이나 정수값 등 직접 입력받아야 할 정보들을 입력 대신 선택할 수 있도록 만들어 줌으로써 값의 범위를 제한하고 무작위로 값이 입력되는 것을 방지하여 코드의 안정성을 높여주는 역할을 한다.
🚀 익스텐션은 기존에 작성된 객체를 직접 수정하지 않고도 기능을 추가하는 방법을 제공한다. 개발에 제약을 줄이고 생산성을 높여주는 방법이라고 할 수 있다.
열거형
하나의 주제로 연관된 데이터들이 멤버로 구성되어 있는 자료형 객체
- 열거형에서 데이터들을 열거형 객체를 정의하는 시점에 함께 정의된다. 따라서 데이터를 함부로 삭제하거나 변경할 수 없으며, 변경하거나 삭제하려면 객체를 정의하는 구문을 직접 수정해야 한다.
- 열거형의 데이터 멤버들은 정의 개념으로 작성되는 것이므로 타입으로 사용할 수 있을 뿐 아니라 컴파일러가 미리 인지할 수도 있다.
- 컴파일러가 미리 인지할 수 있고 없고의 차이는 런타임 오류와 컴파일 오류의 차이로 나타난다. 즉, 열거형을 이용하여 데이터 타입을 정의하고 사용하면 오타나 실수가 발생했을 때 그 즉시 컴파일러가 오류를 찾아주므로 잘못된 점을 바로 확인하여 수정할 수 있지만, 집단 자료형을 사용하여 데이터 타입을 사용하면 잘못 사용했더라도 실행된 다음에야 오류를 알아낼 수 있다.
- 일반적으로 다음의 조건을 만족하는 경우 열거형 객체를 정의해서 사용하는 것이 좋다.
- 원치 않는 값이 잘못 입력되는 것을 막고 싶을 때
- 입력받을 값을 미리 특정할 수 있을 때
- 제한된 값 중에서만 선택할 수 있도록 강제하고 싶을 때
- 열거형의 멤버로 정의할 수 있는 데이터 집합은 연속된 값들이 아닌 불연속된 값들의 집합이어야 하며 공통 주제에 연관되는 값들로 이루어져 있어야 한다.
- 예 ) 성별 : 남, 여 / 국가 : 한국, 일본, 미국, 중국 / 지역 : 서울, 부산, 강원
→ 이 값들의 공통점은 무한히 늘어나지 않고 미리 특정할 수 있는 값들이라는 점이다.
열거형의 정의
enum 열거형 이름 {
// 열거형의 멤버 정의
case 멤버값 1
case 멤버값 2
}
enum Direction {
case north
case south
case east, west // 이렇게 한꺼번에 정의도 가능하다.
}
// 사용
let N = Direction.north
or
let directionToHead: Direction = Direction.west
or
let e: Direction = .east
- 열거형 타입을 생략할 수 있을때와 생략할 수 없을 때
- 열거형 타입으로 정의된 변수에는 열거형 타입명을 생략하고 멤버값만 대입해도 오류가 발생하지 않는다.
- 변수나 상수의 타입 어노테이션을 명시한 경우, 처음부터 타입명을 생략하고 멤버값만 대입해도 오류가 발생하지 않는다.
- 타입 어노테이션 없이 변수나 상수를 초기화할 때는 타입명을 생략할 수 없다.
switch 구문과 열거형
switch 비교 대상 {
case 열거형.멤버1:
// 실행할 구문
case 열거형.멤버2:
// 실행할 구문
}
var directionToHead = Direction.west
switch directionToHead {
case Direction.north:
print("북쪽")
case Direction.south:
print("남쪽")
case Direction.east:
print("동쪽")
case Direction.west:
print("서쪽")
}
or
var directionToHead = Direction.west
switch directionToHead {
case north:
print("북쪽")
case south:
print("남쪽")
case east:
print("동쪽")
case west:
print("서쪽")
}
- 열거형에 정의된 멤버를 switch 구문의 case 블록 비교에 전부 사용하면 default 구문은 생략 가능하다.
멤버와 값의 분리
// 멤버와 값으로 나누어 HTTP 코드를 정의한 열거형의 모습
enum HTTPCode: Int {
case OK = 200
case NOT_MODIFY = 304
case INCORRECT_PAGE = 404
case SERVER_ERROR = 500
}
// 멤버에 별도로 값을 대입할 때에는 멤버에 대입할 값의 자료형을 열거형 타입의 선언 뒤에 타입 어노테이션으로 표기한다.
enum HTTPCode: Int { ..
// 위처럼 표기된 열거형은 멤버가 정수 형태의 값을 할당받는다는 것을 의미한다.
// 멤버값은 일반적으로 문자열이므로 문장열 형식의 멤버에 할당된 값을 읽을 때에는 rawValue 속성을 이용한다.
HTTPCode.OK.rawValue // 200
// 자동 할당 기능
// 열거형 객체에 타입 어노테이션으로 Int를 추가하고 첫 번쨰 멤버에 시작할 정수값을 지정해준다.
enum Rank: Int {
case one = 1
case two,three,four,five
}
// 열거형은 첫 번째 멤버에 지정된 값을 기준으로 나머지 멤버들에게 +1씩 증가시키면서 할당한다.
Rank.one.rawValue // 1
Rank.two.rawValue // 2
Rank.three.rawValue // 3
- rawValue 속성은 열거형 멤버가 값을 저장하고 있을 때에만 사용 가능하다.
- 열거형 객체의 멤버에 보조값을 설정하는 방법 (이렇게 설정된 연관값은 연관 값이라고 한다.)
enum ImageFormat {
case JPEG
case PNG(Bool)
case GIF(Int, Bool)
}
var newImage = ImageFormat.PNG(true)
newImage = .GIF(256, false)
- 열거형은 클래스나 구조체처럼 내부에 연산 프로퍼티와 메소드를 정의할 수 있다. 열거형은 구조체나 클래스와는 달리 인스턴스를 만들 수 없지만 열거형의 멤버를 인스턴스처럼 사용할 수 있으므로 인스턴스 프로퍼티/메소드와 타입 프로퍼티/메소드를 모두 정의할 수 있다.
열거형의 활용
- 피커 컨트롤러에서 소스 타입을 설정할 때 등..
- 이런 값을 열거형으로 정의해두지 안는다면 개발자가 소스타입을 마음대로 입력할 수 있기 때문에 오타나 잘못된 입력으로 인한 오류가 발생할 수 있다.
익스텐션
이미 존재하는 클래스나 구조체, 열거형 등의 객체에 새로운 기능을 추가하여 확장해주는 구문이다.
- 새로운 객체를 정의하는 것이 아니라 이미 존재하는 객체에 여러 요소를 추가해준다는 점이 특징이다.
- 자신의 독립적인 객체를 가지지 않고 다른 객체를 확장해준다는 특징 때문에 확장 구문은 단순히 구문이라고 보기도 객체라고 보기도 어렵다.
- 하지만 확장 구문을 이용하면 거의 제약 없이 새로운 기능을 추가할 수 있다는 점에서 강력한 힘을 가진다.
🚀 익스텐션을 통해 구현할 수 있는 것
- 새로운 연산 프로퍼티 추가
- 새로운 메서드 정의
- 새로운 초기화 구문 추가
- 기존 객체 수정하지 않고 프로토콜 구현
extension <확장할 기존 객체명> {
// 추가할 기능에 대한 구현 코드 작성
}
- 익스텐션을 이용하면 기존 객체에 프로퍼티를 추가할 수 있다. 단, 연산 프로퍼티로 제한된다. 저장 프로퍼티는 익스텐션을 통해 추가할 수 없다.
extension Double {
var km: Double { return self * 1_000.0 }
var m: Double { return self }
var description: Stirng {
return "\(self)km는 \(self)m 이다."
}
2.km // 2000m
7.0.description // "7.0km는 7000.0m 이다."
// 숫자 표현에서 _는 자릿수 구분을 위한 것
1_000 = 1000
익스텐션과 메소드
- 익스텐션을 이용하면 기존 객체에 새로운 인스턴스 메소드나 타입 메소드를 정의할 수 있다. 매개변수 타입을 달리하면 서로 다른 메소드가 되는 메소드 오버로딩 특성을 이용해서 새로운 메소드를 정의할 수도 있고, 매개변수명을 변경하여 새로운 메소드를 작성할 수도 있다.
- 하지만 기존 객체에서 사용된 같은 메소드를 익스텐션에서 재정의하는 것은 안된다.
- 익스텐션은 필요한 곳에서는 충분히 사용하되 남용하지 않고, 여기저기 분산하여 작성하기 보다는 전체적인 정의와 구조를 파악할 수 있는 위치에 작성하는 것이 좋다.
→ 익스텐션을 남용하면 객체의 정의를 모호하게 만들거나 각 실행 위치에 따라 서로 다른 정의로 구성된 객체를 사용하게 만드는 결과를 가져올 수 있기 때문이다.
익스텐션을 활용한 코드 정리
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
public func save(_ value: Any, forKey: String) { }
public func load(_ key: String) -> Any? {
return nil
}
}
- 위와 같이 코드를 작성했을 때 트리
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
}
extension ViewController {
public func save(_ value: Any, forKey: String) { }
public func load(_ key: String) -> Any? {
return nil
}
}
- 익스텐션으로 정의한 메소드의 점프 바
- 위는 나름의 기준으로 메소드를 분류한 것이다. 오버라이드 메소드 or 커스텀 메소드인지의 구분에 따른 것
- 기준은 절대적인 것이 아니라 나름대로의 기준을 정하여도 됨
- 익스텐션에 작성할 수 없는 메소드 : @IBAction 어트리뷰트가 붙는 메소드 → 기존 클래스에 작성해 줄 것
익스텐션과 델리게이트 패턴
- 델리게이트 패턴 구현에는 프로토콜이 사용되기 때문에, 화면 다수 요소에 델리게이트 패턴을 적용할 경우 클래스 소스코드는 엉망이 된다.
- 이 때 프로토콜을 성격에 맞게 여러 개의 익스텐션으로 나누어 구현하면 깔끔하게 코드를 작성할 수 있다.
- 익스텐션 구문을 이용하여 코드를 그룹화 할 때에는 MARK 주석과 함께 사용하는 것이 좋다.
- swift에서 특수 용도로 사용되는 주석 기능 중의 하나로, 여기에 작성되는 내용은 점프 바나 심벌 탐색기에서 표시되기 때문에 익스텐션과 비슷하게 코드를 구분하는데에 유용하게 사용할 수 있다.
// MARK: 테이블 뷰를 위한 프로토콜 델리게이트 구현