[Swift 정면돌파] 15. 익스텐션

H43RO·2021년 8월 8일
1

Swift 정면돌파

목록 보기
15/19
post-thumbnail

익스텐션 개념

  • 익스텐션은 구조체, 클래스, 열거형, 프로토콜 타입에 새로운 기능을 추가 할 수 있는 기능
  • 기능을 추가하려는 타입의 구현 코드를 볼 수 없어도 타입만 알고 있다면 부가적으로 기능을 확장할 수도 있음

익스텐션이 타입에 추가할 수 있는 기능

  • 연산 타입 프로퍼티 / 연산 인스턴스 프로퍼티
  • 타입 메서드 / 인스턴스 메서드
  • 이니셜라이저
  • 서브스크립트
  • 중첩 타입
  • 특정 프로토콜을 준수할 수 있도록 기능 추가
  • 기존에 존재하는 기능을 재정의할 순 없음

클래스의 상속과 익스텐션 비교

  • 클래스의 상속은 클래스 타입에서만 가능하지만 익스텐션은 구조체, 클래스, 프로토콜 등에 적용이 가능함
  • 클래스의 상속은 특정 타입을 물려받아 하나의 새로운 타입을 정의하고 추가 기능을 구현하는 수직 확장이지만, 익스텐션은 기존의 타입에 기능을 추가하는 수평 확장 개념
  • 또, 상속을 받으면 기존 기능을 재정의할 수 있지만, 익스텐션은 재정의할 수 없음 (오버라이딩 불가)

익스텐션 활용

  • 외부 라이브러리나 프레임워크를 가져다 썼다면 보통 원본 소스를 수정하지 못하는데, 이때 외부에서 가져온 타입에 내가 원하는 기능을 추가하고자 할 때 익스텐션을 사용하면 됨
    → 따로 상속을 받지 않아도 되고, 구조체와 열거형에도 기능을 추가할 수 있으므로 편리함
  • 익스텐션은 모든 타입에 적용할 수 있음 (구조체, 열거형, 클래스, 프로토콜, 제네릭 타입 등)
    → 즉, 모든 타입에 연산 프로퍼티, 메서드, 이니셜라이저, 서브스크립트, 중첩 데이터 타입 등 추가 가능
  • 익스텐션은 프로토콜과 함께 사용하면 굉장히 강력해짐 (POP 패러다임 참고)

정의

extension 키워드 + 확장할 타입명 + 필요에 따라 프로토콜 열거

extension 확장할 타입 이름 {
    /* 타입에 추가될 새로운 기능 구현 */
}

// 다른 프로토콜을 채택할 수 있도록 확장할 수도 있음
extension 확장할 타입 이름: 프로토콜1, 프로토콜2, 프로토콜3... {
    /* 프로토콜 요구사항 구현 */
}

구현

1. 연산 프로퍼티 추가해보기

  • 아래 익스텐션은 Int 타입에 두 개의 연산 프로퍼티를 추가한 것
  • Int 타입의 인스턴스가 홀수인지 짝수인지 판별하여 Bool 타입으로 알려주는 연산 프로퍼티
  • 익스텐션으로 Int 타입에 추가해준 연산 프로퍼티는 Int 타입의 어떤 인스턴스에도 사용 가능
  • 인스턴스 연산 프로퍼티를 추가할 수도 있으며, static 키워드를 사용하여 타입 연산 프로퍼티도 가능
extension Int {
    var isEven: Bool {
        return self % 2 == 0
    }
    var isOdd: Bool {
        return self % 2 == 1
    }
}

print(1.isEven) // false
print(2.isEven) // true
print(1.isOdd)  // true
print(2.isOdd)  // false

var number: Int = 3
print(number.isEven) // false
print(number.isOdd) // true

number = 2
print(number.isEven) // true
print(number.isOdd) // false

2. 메소드 추가해보기

  • 메소드 익스텐션을 통해 Int 타입에 인스턴스 메소드인 multiply(by:) 메서드를 추가함
  • 여러 기능을 여러 익스텐션 블록으로 나눠서 구현해도 문제가 없음
  • 관련된 기능별로 (모듈 형식으로) 하나의 익스텐션 블록에 묶어주는 것도 좋음
extension Int {
    func multiply(by n: Int) -> Int {
        return self * n
    }
}
print(3.multiply(by: 2))  // 6
print(4.multiply(by: 5))  // 20

number = 3
print(number.multiply(by: 2))   // 6
print(number.multiply(by: 3))   // 9

3. 이니셜라이저 추가해보기

  • 인스턴스를 초기화할 때 인스턴스 초기화에 필요한 다양한 데이터
    전달받을 수 있도록 여러 종류의 이니셜라이저를 만들 수 있음
    → 타입의 정의부에 이니셜라이저를 추가하지 않더라도 익스텐션을 통해 이니셜라이저를 추가 가능
  • 익스텐션으로 클래스 타입에 편의 이니셜라이저는 추가할 수 있지만, 지정 이니셜라이저는 추가할 수 없음
    → 지정 이니셜라이저와 디이니셜라이저는 반드시 클래스 구현부에 위치해야 함 (값 타입은 상관 X)
extension String {
    init(int: Int) {
        self = "\(int)"
    }
    
    init(double: Double) {
        self = "\(double)"
    }
}

let stringFromInt: String = String(int: 100) 
// "100"

let stringFromDouble: String = String(double: 100.0)    
// "100.0"
profile
어려울수록 기본에 미치고 열광하라

2개의 댓글

comment-user-thumbnail
2021년 8월 9일

글 정리를 잘하시네요. 혹시 XCODE 기본 사용법에 대해서도 작성하실 생각은 없으신가요?

1개의 답글