📱 Mobile/🚀 Swift

[Swift] 열거형과 익스텐션

exception_log 2021. 1. 28. 17:09

<!— 본 포스트는 "꼼꼼한 재은씨의 Swift : 문법편"을 공부하며 직접 정리한 포스트입니다. —>

 

🚀작성자의 Swift 정리 원본인 Notion 구경하기 -> www.notion.so/Swift-59150070adb0467ea11d4f69090dbb24

 

🚀 열거형은 문자열이나 정수값 등 직접 입력받아야 할 정보들을 입력 대신 선택할 수 있도록 만들어 줌으로써 값의 범위를 제한하고 무작위로 값이 입력되는 것을 방지하여 코드의 안정성을 높여주는 역할을 한다.

🚀 익스텐션은 기존에 작성된 객체를 직접 수정하지 않고도 기능을 추가하는 방법을 제공한다. 개발에 제약을 줄이고 생산성을 높여주는 방법이라고 할 수 있다.

열거형

하나의 주제로 연관된 데이터들이 멤버로 구성되어 있는 자료형 객체
  • 열거형에서 데이터들을 열거형 객체를 정의하는 시점에 함께 정의된다. 따라서 데이터를 함부로 삭제하거나 변경할 수 없으며, 변경하거나 삭제하려면 객체를 정의하는 구문을 직접 수정해야 한다.
  • 열거형의 데이터 멤버들은 정의 개념으로 작성되는 것이므로 타입으로 사용할 수 있을 뿐 아니라 컴파일러가 미리 인지할 수도 있다.
  • 컴파일러가 미리 인지할 수 있고 없고의 차이는 런타임 오류와 컴파일 오류의 차이로 나타난다. 즉, 열거형을 이용하여 데이터 타입을 정의하고 사용하면 오타나 실수가 발생했을 때 그 즉시 컴파일러가 오류를 찾아주므로 잘못된 점을 바로 확인하여 수정할 수 있지만, 집단 자료형을 사용하여 데이터 타입을 사용하면 잘못 사용했더라도 실행된 다음에야 오류를 알아낼 수 있다.
  • 일반적으로 다음의 조건을 만족하는 경우 열거형 객체를 정의해서 사용하는 것이 좋다.
    1. 원치 않는 값이 잘못 입력되는 것을 막고 싶을 때
    1. 입력받을 값을 미리 특정할 수 있을 때
    1. 제한된 값 중에서만 선택할 수 있도록 강제하고 싶을 때
  • 열거형의 멤버로 정의할 수 있는 데이터 집합은 연속된 값들이 아닌 불연속된 값들의 집합이어야 하며 공통 주제에 연관되는 값들로 이루어져 있어야 한다.
    • 예 ) 성별 : 남, 여 / 국가 : 한국, 일본, 미국, 중국 / 지역 : 서울, 부산, 강원

    → 이 값들의 공통점은 무한히 늘어나지 않고 미리 특정할 수 있는 값들이라는 점이다.

열거형의 정의

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
  • 열거형 타입을 생략할 수 있을때와 생략할 수 없을 때
    1. 열거형 타입으로 정의된 변수에는 열거형 타입명을 생략하고 멤버값만 대입해도 오류가 발생하지 않는다.
    1. 변수나 상수의 타입 어노테이션을 명시한 경우, 처음부터 타입명을 생략하고 멤버값만 대입해도 오류가 발생하지 않는다.
    1. 타입 어노테이션 없이 변수나 상수를 초기화할 때는 타입명을 생략할 수 없다.

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)
  • 열거형은 클래스나 구조체처럼 내부에 연산 프로퍼티와 메소드를 정의할 수 있다. 열거형은 구조체나 클래스와는 달리 인스턴스를 만들 수 없지만 열거형의 멤버를 인스턴스처럼 사용할 수 있으므로 인스턴스 프로퍼티/메소드와 타입 프로퍼티/메소드를 모두 정의할 수 있다.

열거형의 활용

  • 피커 컨트롤러에서 소스 타입을 설정할 때 등..
  • 이런 값을 열거형으로 정의해두지 안는다면 개발자가 소스타입을 마음대로 입력할 수 있기 때문에 오타나 잘못된 입력으로 인한 오류가 발생할 수 있다.

익스텐션

이미 존재하는 클래스나 구조체, 열거형 등의 객체에 새로운 기능을 추가하여 확장해주는 구문이다.
  • 새로운 객체를 정의하는 것이 아니라 이미 존재하는 객체에 여러 요소를 추가해준다는 점이 특징이다.
  • 자신의 독립적인 객체를 가지지 않고 다른 객체를 확장해준다는 특징 때문에 확장 구문은 단순히 구문이라고 보기도 객체라고 보기도 어렵다.
  • 하지만 확장 구문을 이용하면 거의 제약 없이 새로운 기능을 추가할 수 있다는 점에서 강력한 힘을 가진다.

🚀 익스텐션을 통해 구현할 수 있는 것

  1. 새로운 연산 프로퍼티 추가
  1. 새로운 메서드 정의
  1. 새로운 초기화 구문 추가
  1. 기존 객체 수정하지 않고 프로토콜 구현
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: 테이블 뷰를 위한 프로토콜 델리게이트 구현
반응형

'📱 Mobile > 🚀 Swift' 카테고리의 다른 글

[Swift] 오류 처리  (0) 2021.01.29
[Swift] 프로토콜  (0) 2021.01.29
[Swift] 구조체와 클래스 - (2)  (0) 2021.01.27
[Swift] 구조체와 클래스 - (1)  (0) 2021.01.26
[Swift] 함수 - (3)  (0) 2021.01.25