Swift - 익스텐션

임성빈·2022년 4월 13일
0

Swift

목록 보기
20/26
post-thumbnail
post-custom-banner

익스텐션


익스텐션을 이용해 클래스, 구조체, 열거형 혹은 프로토콜 타입에 기능을 추가할 수 있다. retroative modeling으로 알려진 것과 같이 원본 코드를 몰라도 그 타입에 대한 기능을 확장할 수 있다. Swift에서 익스텐션을 이용해 다음을 할 수 있다.

  • 계산된 인스턴스 프로퍼티와 계산된 타입 프로퍼티의 추가
  • 인스턴스 메소드와 타입 메소드의 추가
  • 새로운 Initializer 제공
  • 서브스크립트 정의
  • 중첩 타입의 선언과 사용
  • 특정 프로토콜을 따르는 타입 생성

익스텐션 문법

인스텐션은 extension 키워드를 사용해 선언한다.

extension SomeType {
	// new functionality to add to SomeType goes here
}

하나의 익스텐션에서 현재 존재하는 타입에 한개 이상의 프로토콜을 따르도록 확장할 수 있다.

extension SomeType: SomeProtocol, AnotherProtocol {
	// implementation of protocol requirements goes here
}

계산된 프로퍼티

익스텐션을 이용해 존재하는 타입에 계산된 인스턴스 프로퍼티와 타입 프로퍼티를 추가할 수 있다. 다음 예제는 Swift의 built-in 타입인 Double에 5개의 계산된 인스턴스 프로퍼티를 추가한는 예제이다.

extension Double {
	var km: Double { return self 1_000.0 }
	var m: Double { return self }
	var cm: Double { return self / 100.0 }
	var mm: Double { return self / 1_000.0 }
	var ft: Double { return self / 3.28084 }
}
let oneInch = 25.4.mm
print("One inch is \(oneInch) meters")
// "One inch is 0.0254 meters" 출력
let threeFeet = 3.ft
print("Three feet is \(threeFeet) meters")
// "Three feet is 0.914399970739201 meters" 출력

주어진 Double 값에 km , m , cm 등 단위를 붙여 미터로 변경하는 계산된 프로퍼티이다. 단위 변환은 m 를 기준으로 한다. 이 프로퍼티들은 읽기 전용 계산된 프로퍼티이기 때문에 간결함을 위해 get 을 붙이지 않는다.

let aMarathon =42.km + 195.m
print("A marathon is \(aMarathon) meters long")
// "A marathon is \(aMarathon) meters long" 출력

단위 변환값이 Double 타입이기 때문에 각각의 변환값의 연산도 가능하다.


Initializer

익스텐션을 이용해 존재하는 타입에 새로운 Initializer를 추가할 수 있다. 이 방법으로 커스텀 타입의 Initializer 파라미터를 넣을 수 있도록 변경하거나 원래 구현에서 포함하지 않는 초기화 정보를 추가할 수 있다.

익스텐션은 클래스에 새로운 편리 Initializer를 추가할 수 있지만 지정 Initializer나 Deinitializer를 추가할 수는 없다. 지정 Initializer는 항상 오리지널 클래스의 구현에서 작성해야한다.

만약 익스텐션을 값타입에 Initializer를 추가하는데 사용하고, 그 값타입이 모든 프로퍼티에 대해 기본값을 제공하고 커스텀 Initializer를 정의하지 않았다면, 익스텐션에서 기본 Initializer와 멤버쪽 Initializer를 익스텐션에서 호출할 수 있다. 만약 값타입의 오리지널 구현의 부분으로 Initializer 코드를 작성했다면 그것은 이 경우에 해당하지 않는다.

만약 다른 모듈에 선언딘 구조체에 Initializer를 추가하는 익스텐션을 사용한다면 새로운 Initializer는 모듈에 정의된 Initializer를 호출하기 전까지 self에 접근할 수 없다.

struct Size {
	var width = 0.0, height = 0.0
}
struct Point {
	var x = 0.0, y = 0.0
}
struct Rect {
	var origin = Point()
    var size = Size()
}

위 예제에서는 SizePoint 구조체를 정의하고 그것을 사용하는 Rect 구조체를 정의했다. Rect 구조체에서 모든 프로퍼티의 기본값을 제공하기 때문에 Rect 구조체는 기본 Initializer와 멤버쪽 Initializer를 자동으로 제공받아 사용할 수 있다.

기본적으로 제공되는 Initializer를 사용해 초기화를 한 예제이다.

let defaultRect = Rect()
let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0), size: Size(width: 5.0, height:5.0))

Rect 구조체를 추가적인 Initializer를 제공하기 위해 확장할 수 있다.

extension Rect {
	init(center: Point, size: Size) {
    	let originX = center.x - (size.width / 2)
        let originY = center.y - (size.height / 2)
        self.init(origin: Point(x: originX, y: originY), size: size)
    }
}

Rect 에서 확장한 Initializer를 사용한 코드는 다음과 같이 사용할 수 있다.

let centerRect = Rect(center: Point(x: 4.0, y: 4.0), size: Size(width: 3.0, height: 3.0))

메소드

익스텐션을 이용해 존재하는 타입에 인스턴스 메소드나 타입 메소드를 추가할 수 있다. 다음 예제는 Int 타입에 repertitions 라는 인스턴스 메소드를 추가한 예제이다.

extension Int {
	func repetitions(task: () -> Void) {
    	for _ in 0..<self {
        	task()
        }
    }
}

repetitions(task: ) 메소드는 ()-> Void 타입의 하나의 인자를 받고 파라미터와 반환값이 없는 함수이다.

3.repetitions {
	print("Hello")
}
// "Hello"
// "Hello"
// "Hello"

함수를 실행하면 함수 안의 task 를 숫자만큼 반복 실행한다.


변경 가능한 인스턴스 메소드

익스텐션에서 추가된 인스턴스 메소드는 인스턴스 자신을 변경할 수 있다. 구조체와 열거형 메소드 중 자신을 변경하는 인스턴스 메소드는 원본 구현의 mutating 메소드와 같이 반드시 mutating 으로 선언돼야 한다.

extension Int {
	mutating func square() {
    	self = self * self
    }
}
var someInt = 3
someInt.square()
// someInt = 9

서브스크립트

익스텐션을 이용해 존재하는 타입에 새로운 서브스크립트를 추가할 수 있다. 다음 예제는 Swift의 built-in 타입에 integer 서브스크립트를 추가한 예제이다. 서브스크립트 [n]은 숫자의 n번째 자리수를 반환한다.

extension Int {
    subscript(digitIndex: Int) -> Int {
        var decimalBase = 1
        for _ in 0..<digitIndex {
            decimalBase *= 10
        }
        return (self / decimalBase) % 10
        // 10 * n번째 수로 현재 수를 나눈 것의 나머지
    	// 1인 경우 746381295 % 10 -> 5가 나머지
    	// 2인 경우 746381295 % 10 -> 9가 나머지
    }
}
746381295[0]
// returns 5
746381295[1]
// returns 9
746381295[2]
// returns 2
746381295[8]
// returns 7

// 만약 Int값에서 요청한 값이 처리할 수 있는 자리수를 넘어가면
// 서브스크립트 구현에서 0을 반환한다.

746381295[9]
// return 0

중첩 타입

익스텐션을 이용햏 존재하는 클래스, 구조체, 열겨형에 중첩 타입을 추가할 수 있다.

extension Int {
	enum Kind {
    	case negative, zero, positive
    }
    var kind: Kind {
    	switch self {
        case 0:
    	    return .zero
        case let x where x > 0:
	        return .positive
        default:
        	return .negative
        }
    }
}

위 예제는 Int에 중첩형 enum을 추가한 예제이다. Kind 라고 불리는 열거형은 Int를 음수, 0, 양수로 표현한다.

아래 예제는 새로운 계산된 프로퍼티 kind 를 이용해 특정수가 음수, 0, 양수 중 어떤 것인지를 나타내는 예제이다.

func printIntegerKinds(_ numbers: [Int]) {
    for number in numbers {
        switch number.kind {
        case .negative:
            print("- ", terminator: "")
        case .zero:
            print("0 ", terminator: "")
        case .positive:
            print("+ ", terminator: "")
        }
    }
    print("")
}
printIntegerKinds([3, 19, -27, 0, -6, 0, 7])
// Prints "+ + - 0 - 0 + "

printIntegerKinds(_: ) 함수를 Int 배열을 입력으로 받아 각 Int가 음수, 0, 양수 어디에 속하는지 계산해서 그에 맞는 기호를 반환하는 함수이다.

profile
iOS 앱개발
post-custom-banner

0개의 댓글